mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 00:02:15 +01:00
remove billable_rate_update_time_entries flag and always update all time entries
This commit is contained in:
committed by
Constantin Graf
parent
1dc35f1f55
commit
7aab3d98fc
@@ -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) =>
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user