remove billable_rate_update_time_entries flag and always update all time entries

This commit is contained in:
Gregor Vostrak
2024-07-15 15:53:18 +02:00
committed by Constantin Graf
parent 1dc35f1f55
commit 7aab3d98fc
14 changed files with 34 additions and 241 deletions

View File

@@ -76,42 +76,6 @@ test('test that error shows if no role is selected', async ({ page }) => {
]);
});
test('test that member billable rate can be updated', async ({ page }) => {
await goToMembersPage(page);
const newBillableRate = Math.round(Math.random() * 10000);
await page.getByRole('row').first().getByRole('button').click();
await page.getByRole('button').getByText('Edit').first().click();
await page.getByText('Organization Default Rate').click();
await page.getByText('Custom Rate').click();
await page
.getByPlaceholder('Billable Rate')
.fill(newBillableRate.toString());
await page.getByRole('button', { name: 'Update Member' }).click();
await Promise.all([
page
.getByRole('button', { name: 'No, only for new time entries' })
.click(),
page.waitForRequest(
async (request) =>
request.url().includes('/members/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
false
),
page.waitForResponse(
async (response) =>
response.url().includes('/organizations/') &&
response.request().method() === 'PUT' &&
response.status() === 200 &&
(await response.json()).data.billable_rate ===
newBillableRate * 100
),
]);
});
test('test that organization billable rate can be updated with all existing time entries', async ({
page,
}) => {
@@ -132,10 +96,7 @@ test('test that organization billable rate can be updated with all existing time
async (request) =>
request.url().includes('/members/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
true
request.postDataJSON().billable_rate === newBillableRate * 100
),
page.waitForResponse(
async (response) =>

View File

@@ -17,43 +17,6 @@ test('test that organization name can be updated', async ({ page }) => {
).toContainText('NEW ORG NAME');
});
test('test that organization billable rate can be updated', async ({
page,
}) => {
await goToOrganizationSettings(page);
const newBillableRate = Math.round(Math.random() * 10000);
await page.getByLabel('Organization Billable Rate').click();
await page
.getByLabel('Organization Billable Rate')
.fill(newBillableRate.toString());
await page
.locator('button')
.filter({ hasText: /^Save$/ })
.click();
await Promise.all([
page
.getByRole('button', { name: 'No, only for new time entries' })
.click(),
page.waitForRequest(
async (request) =>
request.url().includes('/organizations/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
false
),
page.waitForResponse(
async (response) =>
response.url().includes('/organizations/') &&
response.request().method() === 'PUT' &&
response.status() === 200 &&
(await response.json()).data.billable_rate ===
newBillableRate * 100
),
]);
});
test('test that organization billable rate can be updated with all existing time entries', async ({
page,
}) => {
@@ -75,10 +38,7 @@ test('test that organization billable rate can be updated with all existing time
async (request) =>
request.url().includes('/organizations/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
true
request.postDataJSON().billable_rate === newBillableRate * 100
),
page.waitForResponse(
async (response) =>

View File

@@ -7,66 +7,6 @@ async function goToProjectsOverview(page: Page) {
await page.goto(PLAYWRIGHT_BASE_URL + '/projects');
}
test('test that updating project member billable rate works', async ({
page,
}) => {
const newProjectName =
'New Project ' + Math.floor(1 + Math.random() * 10000);
const newBillableRate = Math.round(Math.random() * 10000);
await goToProjectsOverview(page);
await page.getByRole('button', { name: 'Create Project' }).click();
await page.getByLabel('Project Name').fill(newProjectName);
await page.getByRole('button', { name: 'Create Project' }).nth(1).click();
await expect(page.getByText(newProjectName)).toBeVisible();
await page.getByText(newProjectName).click();
await page.getByRole('button', { name: 'Add Member' }).click();
await expect(page.getByText('Add Project Member').first()).toBeVisible();
await page.keyboard.press('Enter');
await page.getByRole('button', { name: 'Add Project Member' }).click();
await page
.getByTestId('project_member_table')
.getByRole('row')
.first()
.getByRole('button')
.click();
await page.getByRole('button', { name: 'Edit' }).first().click();
await page.getByLabel('Billable Rate').fill(newBillableRate.toString());
await page.getByRole('button', { name: 'Update Project Member' }).click();
await Promise.all([
page
.getByRole('button', { name: 'No, only for new time entries' })
.click(),
page.waitForRequest(
async (request) =>
request.url().includes('/project-members/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
false
),
page.waitForResponse(
async (response) =>
response.url().includes('/project-members/') &&
response.request().method() === 'PUT' &&
response.status() === 200 &&
(await response.json()).data.billable_rate ===
newBillableRate * 100
),
]);
await expect(
page
.getByRole('row')
.first()
.getByText(formatCents(newBillableRate * 100))
).toBeVisible();
});
test('test that updating project member billable rate works for existing time entries', async ({
page,
}) => {
@@ -103,10 +43,7 @@ test('test that updating project member billable rate works for existing time en
async (request) =>
request.url().includes('/project-members/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
true
request.postDataJSON().billable_rate === newBillableRate * 100
),
page.waitForResponse(
async (response) =>

View File

@@ -86,56 +86,6 @@ test('test that archiving and unarchiving projects works', async ({ page }) => {
]);
});
test('test that updating billable rate works', async ({ page }) => {
const newProjectName =
'New Project ' + Math.floor(1 + Math.random() * 10000);
const newBillableRate = Math.round(Math.random() * 10000);
await goToProjectsOverview(page);
await page.getByRole('button', { name: 'Create Project' }).click();
await page.getByLabel('Project Name').fill(newProjectName);
await page.getByRole('button', { name: 'Create Project' }).nth(1).click();
await expect(page.getByText(newProjectName)).toBeVisible();
await page.getByRole('row').first().getByRole('button').click();
await page.getByRole('button').getByText('Edit').first().click(),
await page.getByText('Non-Billable').click();
await page.getByText('Custom Rate').click();
await page
.getByPlaceholder('Billable Rate')
.fill(newBillableRate.toString());
await page.getByRole('button', { name: 'Update Project' }).click();
await Promise.all([
page
.getByRole('button', { name: 'No, only for new time entries' })
.click(),
page.waitForRequest(
async (request) =>
request.url().includes('/projects/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
false
),
page.waitForResponse(
async (response) =>
response.url().includes('/projects/') &&
response.request().method() === 'PUT' &&
response.status() === 200 &&
(await response.json()).data.billable_rate ===
newBillableRate * 100
),
]);
await expect(
page
.getByRole('row')
.first()
.getByText(formatCents(newBillableRate * 100))
).toBeVisible();
});
test('test that updating billable rate works with existing time entries', async ({
page,
}) => {
@@ -166,10 +116,7 @@ test('test that updating billable rate works with existing time entries', async
async (request) =>
request.url().includes('/projects/') &&
request.method() === 'PUT' &&
request.postDataJSON().billable_rate ===
newBillableRate * 100 &&
request.postDataJSON().billable_rate_update_time_entries ===
true
request.postDataJSON().billable_rate === newBillableRate * 100
),
page.waitForResponse(
async (response) =>

View File

@@ -36,11 +36,7 @@ const MemberPivotResource = z
})
.passthrough();
const updateMember_Body = z
.object({
role: Role,
billable_rate: z.union([z.number(), z.null()]),
billable_rate_update_time_entries: z.boolean(),
})
.object({ role: Role, billable_rate: z.union([z.number(), z.null()]) })
.partial()
.passthrough();
const MemberResource = z
@@ -66,7 +62,6 @@ const updateOrganization_Body = z
.object({
name: z.string(),
billable_rate: z.union([z.number(), z.null()]).optional(),
billable_rate_update_time_entries: z.boolean().optional(),
})
.passthrough();
const ProjectResource = z
@@ -97,7 +92,6 @@ const updateProject_Body = z
is_archived: z.boolean().optional(),
client_id: z.union([z.string(), z.null()]).optional(),
billable_rate: z.union([z.number(), z.null()]).optional(),
billable_rate_update_time_entries: z.boolean().optional(),
})
.passthrough();
const ProjectMemberResource = z
@@ -115,10 +109,7 @@ const createProjectMember_Body = z
})
.passthrough();
const updateProjectMember_Body = z
.object({
billable_rate: z.union([z.number(), z.null()]),
billable_rate_update_time_entries: z.boolean(),
})
.object({ billable_rate: z.union([z.number(), z.null()]) })
.partial()
.passthrough();
const TagResource = z
@@ -869,6 +860,17 @@ const endpoints = makeApi([
],
response: z.object({ data: MemberResource }).passthrough(),
errors: [
{
status: 400,
description: `API exception`,
schema: z
.object({
error: z.boolean(),
key: z.string(),
message: z.string(),
})
.passthrough(),
},
{
status: 403,
description: `Authorization error`,

View File

@@ -8,7 +8,7 @@ const show = defineModel('show', { default: false });
const saving = defineModel('saving', { default: false });
const emit = defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
submit: [];
}>();
defineProps<{
@@ -31,15 +31,9 @@ defineProps<{
<PrimaryButton
:class="{ 'opacity-25': saving }"
:disabled="saving"
@click="emit('submit', true)">
@click="emit('submit')">
Yes, update existing time entries
</PrimaryButton>
<PrimaryButton
:class="{ 'opacity-25': saving }"
:disabled="saving"
@click="emit('submit', false)">
No, only for new time entries
</PrimaryButton>
</div>
<p class="text-center pt-3 pb-1">
Learn more about the

View File

@@ -11,13 +11,13 @@ defineProps<{
}>();
defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
submit: [];
}>();
</script>
<template>
<BillableRateModal
@submit="(...args) => $emit('submit', ...args)"
@submit="$emit('submit')"
v-model:show="show"
v-model:saving="saving"
title="Update Member Billable Rate">

View File

@@ -27,9 +27,7 @@ const memberBody = ref<UpdateMemberBody>({
billable_rate: props.member.billable_rate,
});
async function submitBillableRate(billableRateUpdateTimeEntries: boolean) {
memberBody.value.billable_rate_update_time_entries =
billableRateUpdateTimeEntries;
async function submitBillableRate() {
if (memberBody.value.role === 'owner' && props.member.role !== 'owner') {
show.value = false;
showOwnershipTransferConfirmModal.value = true;
@@ -59,7 +57,7 @@ function saveWithChecks() {
show.value = false;
showOwnershipTransferConfirmModal.value = true;
} else {
submitBillableRate(false);
submitBillableRate();
}
}

View File

@@ -10,13 +10,13 @@ defineProps<{
}>();
defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
submit: [];
}>();
</script>
<template>
<BillableRateModal
@submit="(...args) => $emit('submit', ...args)"
@submit="$emit('submit')"
v-model:show="show"
v-model:saving="saving"
title="Update Organization Billable Rate">

View File

@@ -11,13 +11,13 @@ defineProps<{
}>();
defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
submit: [];
}>();
</script>
<template>
<BillableRateModal
@submit="(...args) => $emit('submit', ...args)"
@submit="$emit('submit')"
v-model:show="show"
v-model:saving="saving"
title="Update Project Billable Rate">

View File

@@ -56,9 +56,7 @@ const currentClientName = computed(() => {
return 'No Client';
});
async function submitBillableRate(billableRateUpdateTimeEntries: boolean) {
project.value.billable_rate_update_time_entries =
billableRateUpdateTimeEntries;
async function submitBillableRate() {
await updateProject(props.originalProject.id, project.value);
show.value = false;
showBillableRateModal.value = false;

View File

@@ -7,17 +7,17 @@ const saving = defineModel('saving', { default: false });
defineProps<{
newBillableRate?: number | null;
memberName: string;
memberName?: string;
}>();
defineEmits<{
submit: [billable_rate_update_time_entries: boolean];
submit: [];
}>();
</script>
<template>
<BillableRateModal
@submit="(...args) => $emit('submit', ...args)"
@submit="$emit('submit')"
v-model:show="show"
v-model:saving="saving"
title="Update Project Member Billable Rate">

View File

@@ -39,9 +39,7 @@ async function submit() {
};
}
async function submitBillableRate(billableRateUpdateTimeEntries: boolean) {
projectMemberBody.value.billable_rate_update_time_entries =
billableRateUpdateTimeEntries;
async function submitBillableRate() {
await updateProjectMember(props.projectMember.id, projectMemberBody.value);
show.value = false;
showBillableRateModal.value = false;
@@ -73,7 +71,7 @@ useFocus(projectNameInput, { initialValue: true });
<template #content>
<ProjectMemberBillableRateModal
member-name="props.name"
:member-name="props.name"
v-model:show="showBillableRateModal"
@close="showBillableRateModal = false"
@submit="submitBillableRate"></ProjectMemberBillableRateModal>

View File

@@ -27,10 +27,8 @@ onMounted(async () => {
});
const showConfirmationModal = ref(false);
async function submit(billableRateUpdateTimeEntries: boolean) {
async function submit() {
saving.value = true;
organizationBody.value.billable_rate_update_time_entries =
billableRateUpdateTimeEntries;
await updateOrganization(organizationBody.value);
saving.value = false;
showConfirmationModal.value = false;