BETA for new DB
This commit is contained in:
@@ -155,7 +155,6 @@ const links = computed(() => {
|
|||||||
label: "Anwesenheiten",
|
label: "Anwesenheiten",
|
||||||
to: "/staff/time",
|
to: "/staff/time",
|
||||||
icon: "i-heroicons-clock",
|
icon: "i-heroicons-clock",
|
||||||
disabled: true
|
|
||||||
}] : [],
|
}] : [],
|
||||||
/*... has("absencerequests") ? [{
|
/*... has("absencerequests") ? [{
|
||||||
label: "Abwesenheiten",
|
label: "Abwesenheiten",
|
||||||
@@ -186,7 +185,6 @@ const links = computed(() => {
|
|||||||
label: "Eingangsbelege",
|
label: "Eingangsbelege",
|
||||||
to: "/incomingInvoices",
|
to: "/incomingInvoices",
|
||||||
icon: "i-heroicons-document-text",
|
icon: "i-heroicons-document-text",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "Kostenstellen",
|
label: "Kostenstellen",
|
||||||
to: "/standardEntity/costcentres",
|
to: "/standardEntity/costcentres",
|
||||||
@@ -195,7 +193,6 @@ const links = computed(() => {
|
|||||||
label: "Buchungskonten",
|
label: "Buchungskonten",
|
||||||
to: "/accounts",
|
to: "/accounts",
|
||||||
icon: "i-heroicons-document-text",
|
icon: "i-heroicons-document-text",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "zusätzliche Buchungskonten",
|
label: "zusätzliche Buchungskonten",
|
||||||
to: "/standardEntity/ownaccounts",
|
to: "/standardEntity/ownaccounts",
|
||||||
@@ -205,7 +202,6 @@ const links = computed(() => {
|
|||||||
label: "Bank",
|
label: "Bank",
|
||||||
to: "/banking",
|
to: "/banking",
|
||||||
icon: "i-heroicons-document-text",
|
icon: "i-heroicons-document-text",
|
||||||
disabled: true
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
@@ -312,7 +308,6 @@ const links = computed(() => {
|
|||||||
label: "Nummernkreise",
|
label: "Nummernkreise",
|
||||||
to: "/settings/numberRanges",
|
to: "/settings/numberRanges",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
disabled: true
|
|
||||||
},/*{
|
},/*{
|
||||||
label: "Rollen",
|
label: "Rollen",
|
||||||
to: "/roles",
|
to: "/roles",
|
||||||
@@ -321,17 +316,14 @@ const links = computed(() => {
|
|||||||
label: "E-Mail Konten",
|
label: "E-Mail Konten",
|
||||||
to: "/settings/emailaccounts",
|
to: "/settings/emailaccounts",
|
||||||
icon: "i-heroicons-envelope",
|
icon: "i-heroicons-envelope",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "Bankkonten",
|
label: "Bankkonten",
|
||||||
to: "/settings/banking",
|
to: "/settings/banking",
|
||||||
icon: "i-heroicons-currency-euro",
|
icon: "i-heroicons-currency-euro",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "Textvorlagen",
|
label: "Textvorlagen",
|
||||||
to: "/settings/texttemplates",
|
to: "/settings/texttemplates",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
disabled: true
|
|
||||||
},/*{
|
},/*{
|
||||||
label: "Eigene Felder",
|
label: "Eigene Felder",
|
||||||
to: "/settings/ownfields",
|
to: "/settings/ownfields",
|
||||||
@@ -340,12 +332,10 @@ const links = computed(() => {
|
|||||||
label: "Firmeneinstellungen",
|
label: "Firmeneinstellungen",
|
||||||
to: "/settings/tenant",
|
to: "/settings/tenant",
|
||||||
icon: "i-heroicons-building-office",
|
icon: "i-heroicons-building-office",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "Projekttypen",
|
label: "Projekttypen",
|
||||||
to: "/projecttypes",
|
to: "/projecttypes",
|
||||||
icon: "i-heroicons-clipboard-document-list",
|
icon: "i-heroicons-clipboard-document-list",
|
||||||
disabled: true
|
|
||||||
},{
|
},{
|
||||||
label: "Export",
|
label: "Export",
|
||||||
to: "/export",
|
to: "/export",
|
||||||
|
|||||||
@@ -3,55 +3,75 @@ import dayjs from "dayjs";
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
entry?: null;
|
entry?: any | null;
|
||||||
|
users: any[];
|
||||||
|
canSelectUser: boolean;
|
||||||
|
defaultUserId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue", "saved"]);
|
const emit = defineEmits(["update:modelValue", "saved"]);
|
||||||
|
|
||||||
const { create, update } = useStaffTime();
|
const { create, update } = useStaffTime();
|
||||||
|
|
||||||
// v-model für das Modal
|
|
||||||
const show = computed({
|
const show = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (v: boolean) => emit("update:modelValue", v),
|
set: (v: boolean) => emit("update:modelValue", v),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🧱 Lokale reactive Kopie, die beim Öffnen aus props.entry befüllt wird
|
// 🌈 Typen
|
||||||
const local = reactive<{
|
const typeOptions = [
|
||||||
id?: string;
|
{ label: "Arbeitszeit", value: "work" },
|
||||||
description: string;
|
{ label: "Urlaub", value: "vacation" },
|
||||||
started_at: string;
|
{ label: "Krankheit", value: "sick" },
|
||||||
stopped_at: string | null;
|
{ label: "Feiertag", value: "holiday" },
|
||||||
type: string;
|
];
|
||||||
}>({
|
|
||||||
|
// Lokaler State
|
||||||
|
const local = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
|
user_id: "", // 👈 Mitarbeiter
|
||||||
description: "",
|
description: "",
|
||||||
started_at: dayjs().startOf("hour").format("YYYY-MM-DDTHH:mm"),
|
started_at: "",
|
||||||
stopped_at: dayjs().add(1, "hour").format("YYYY-MM-DDTHH:mm"),
|
stopped_at: "",
|
||||||
type: "work",
|
type: "work",
|
||||||
|
vacation_reason: "",
|
||||||
|
sick_reason: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 📡 Wenn das Modal geöffnet wird, Entry-Daten übernehmen
|
// 📡 ENTRY —> LOCAL
|
||||||
watch(
|
watch(
|
||||||
() => props.entry,
|
() => props.entry,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
Object.assign(local, {
|
Object.assign(local, {
|
||||||
id: val.id,
|
id: val.id,
|
||||||
|
user_id: val.user_id, // 👈 Mitarbeiter vorbelegen
|
||||||
description: val.description || "",
|
description: val.description || "",
|
||||||
started_at: dayjs(val.started_at).format("YYYY-MM-DDTHH:mm"),
|
|
||||||
stopped_at: val.stopped_at
|
|
||||||
? dayjs(val.stopped_at).format("YYYY-MM-DDTHH:mm")
|
|
||||||
: dayjs(val.started_at).add(1, "hour").format("YYYY-MM-DDTHH:mm"),
|
|
||||||
type: val.type || "work",
|
type: val.type || "work",
|
||||||
|
|
||||||
|
started_at:
|
||||||
|
val.type === "vacation"
|
||||||
|
? dayjs(val.started_at).format("YYYY-MM-DD")
|
||||||
|
: dayjs(val.started_at).format("YYYY-MM-DDTHH:mm"),
|
||||||
|
|
||||||
|
stopped_at:
|
||||||
|
val.type === "vacation"
|
||||||
|
? dayjs(val.stopped_at).format("YYYY-MM-DD")
|
||||||
|
: dayjs(val.stopped_at).format("YYYY-MM-DDTHH:mm"),
|
||||||
|
|
||||||
|
vacation_reason: val.vacation_reason || "",
|
||||||
|
sick_reason: val.sick_reason || "",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Object.assign(local, {
|
Object.assign(local, {
|
||||||
id: "",
|
id: "",
|
||||||
|
user_id: props.defaultUserId, // 👈 Neuer Eintrag → aktueller Nutzer
|
||||||
description: "",
|
description: "",
|
||||||
started_at: dayjs().startOf("hour").format("YYYY-MM-DDTHH:mm"),
|
|
||||||
stopped_at: dayjs().add(1, "hour").format("YYYY-MM-DDTHH:mm"),
|
|
||||||
type: "work",
|
type: "work",
|
||||||
|
started_at: dayjs().format("YYYY-MM-DDTHH:mm"),
|
||||||
|
stopped_at: dayjs().add(1, "hour").format("YYYY-MM-DDTHH:mm"),
|
||||||
|
vacation_reason: "",
|
||||||
|
sick_reason: "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -63,13 +83,27 @@ const loading = ref(false);
|
|||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload: any = {
|
||||||
description: local.description,
|
user_id: local.user_id, // 👈 immer senden
|
||||||
started_at: dayjs(local.started_at).toISOString(),
|
|
||||||
stopped_at: local.stopped_at ? dayjs(local.stopped_at).toISOString() : null,
|
|
||||||
type: local.type,
|
type: local.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (local.type === "vacation") {
|
||||||
|
payload.started_at = dayjs(local.started_at).startOf("day").toISOString();
|
||||||
|
payload.stopped_at = dayjs(local.stopped_at).endOf("day").toISOString();
|
||||||
|
payload.vacation_reason = local.vacation_reason;
|
||||||
|
} else {
|
||||||
|
payload.started_at = dayjs(local.started_at).toISOString();
|
||||||
|
payload.stopped_at = local.stopped_at
|
||||||
|
? dayjs(local.stopped_at).toISOString()
|
||||||
|
: null;
|
||||||
|
payload.description = local.description;
|
||||||
|
|
||||||
|
if (local.type === "sick") {
|
||||||
|
payload.sick_reason = local.sick_reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (local.id) {
|
if (local.id) {
|
||||||
await update(local.id, payload);
|
await update(local.id, payload);
|
||||||
} else {
|
} else {
|
||||||
@@ -84,6 +118,7 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UModal v-model="show" :ui="{ width: 'w-full sm:max-w-md' }" :key="local.id || 'new'">
|
<UModal v-model="show" :ui="{ width: 'w-full sm:max-w-md' }" :key="local.id || 'new'">
|
||||||
<UCard>
|
<UCard>
|
||||||
@@ -94,18 +129,72 @@ async function handleSubmit() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<UForm @submit.prevent="handleSubmit" class="space-y-4">
|
<UForm @submit.prevent="handleSubmit" class="space-y-4">
|
||||||
<UFormGroup label="Beschreibung" name="description">
|
|
||||||
<UInput v-model="local.description" placeholder="Was wurde gemacht?" />
|
<!-- 👥 Mitarbeiter-Auswahl -->
|
||||||
|
<UFormGroup label="Mitarbeiter" v-if="props.canSelectUser">
|
||||||
|
<USelectMenu
|
||||||
|
v-model="local.user_id"
|
||||||
|
:options="props.users.map(u => ({
|
||||||
|
label: u.full_name || u.email,
|
||||||
|
value: u.user_id
|
||||||
|
}))"
|
||||||
|
placeholder="Mitarbeiter wählen"
|
||||||
|
option-attribute="label"
|
||||||
|
value-attribute="value"
|
||||||
|
/>
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Startzeit" name="started_at">
|
<!-- TYPE -->
|
||||||
<UInput v-model="local.started_at" type="datetime-local" />
|
<UFormGroup label="Typ">
|
||||||
|
<USelect v-model="local.type" :options="typeOptions" />
|
||||||
</UFormGroup>
|
</UFormGroup>
|
||||||
|
|
||||||
<UFormGroup label="Endzeit" name="stopped_at">
|
<!-- VACATION -->
|
||||||
<UInput v-model="local.stopped_at" type="datetime-local" />
|
<template v-if="local.type === 'vacation'">
|
||||||
</UFormGroup>
|
<UFormGroup label="Urlaubsgrund">
|
||||||
|
<UInput v-model="local.vacation_reason" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Start (Tag)">
|
||||||
|
<UInput v-model="local.started_at" type="date" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Ende (Tag)">
|
||||||
|
<UInput v-model="local.stopped_at" type="date" />
|
||||||
|
</UFormGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- SICK -->
|
||||||
|
<template v-else-if="local.type === 'sick'">
|
||||||
|
<UFormGroup label="Krankheitsgrund">
|
||||||
|
<UInput v-model="local.sick_reason" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Start (Tag)">
|
||||||
|
<UInput v-model="local.started_at" type="date" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Ende (Tag)">
|
||||||
|
<UInput v-model="local.stopped_at" type="date" />
|
||||||
|
</UFormGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- WORK / OTHER -->
|
||||||
|
<template v-else>
|
||||||
|
<UFormGroup label="Beschreibung">
|
||||||
|
<UInput v-model="local.description" placeholder="Was wurde gemacht?" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Startzeit">
|
||||||
|
<UInput v-model="local.started_at" type="datetime-local" />
|
||||||
|
</UFormGroup>
|
||||||
|
|
||||||
|
<UFormGroup label="Endzeit">
|
||||||
|
<UInput v-model="local.stopped_at" type="datetime-local" />
|
||||||
|
</UFormGroup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- ACTIONS -->
|
||||||
<div class="flex justify-end gap-2 mt-4">
|
<div class="flex justify-end gap-2 mt-4">
|
||||||
<UButton color="gray" label="Abbrechen" @click="show = false" />
|
<UButton color="gray" label="Abbrechen" @click="show = false" />
|
||||||
<UButton color="primary" :loading="loading" type="submit" label="Speichern" />
|
<UButton color="primary" :loading="loading" type="submit" label="Speichern" />
|
||||||
@@ -114,3 +203,4 @@ async function handleSubmit() {
|
|||||||
</UCard>
|
</UCard>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export const useEntities = (
|
|||||||
) => {
|
) => {
|
||||||
if (!idToEq) return null
|
if (!idToEq) return null
|
||||||
|
|
||||||
const res = await useNuxtApp().$api(withInformation ? `/api/resource/${relation}/${idToEq}/${withInformation}` : `/api/resource/${relation}/${idToEq}`, {
|
const res = await useNuxtApp().$api(withInformation ? `/api/resource/${relation}/${idToEq}` : `/api/resource/${relation}/${idToEq}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
params: { select }
|
params: { select }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,4 +5,21 @@ export const useFormatDuration = (durationInMinutes:number,) => {
|
|||||||
const mins = Math.floor(durationInMinutes % 60)
|
const mins = Math.floor(durationInMinutes % 60)
|
||||||
|
|
||||||
return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(2, "0")}`
|
return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(2, "0")}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFormatDurationDays = (start,end) => {
|
||||||
|
const startDate = useNuxtApp().$dayjs(start);
|
||||||
|
const endDate = useNuxtApp().$dayjs(end);
|
||||||
|
|
||||||
|
if(startDate.isBefore(endDate)){
|
||||||
|
// inkl. beider Tage → +1
|
||||||
|
const days = endDate.diff(startDate, "day") + 1;
|
||||||
|
|
||||||
|
return days + " Tag" + (days > 1 ? "e" : "");
|
||||||
|
} else {
|
||||||
|
const days = startDate.diff(endDate, "day") + 1;
|
||||||
|
|
||||||
|
return days + " Tag" + (days > 1 ? "e" : "");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ interface StaffTimeEntry {
|
|||||||
|
|
||||||
export function useStaffTime() {
|
export function useStaffTime() {
|
||||||
const { $api } = useNuxtApp()
|
const { $api } = useNuxtApp()
|
||||||
|
const auth = useAuthStore()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -46,9 +47,17 @@ export function useStaffTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function approve(id: string) {
|
async function approve(id: string) {
|
||||||
return await $api<StaffTimeEntry>(`/api/staff/time/${id}`, {
|
const auth = useAuthStore()
|
||||||
|
const now = useNuxtApp().$dayjs().toISOString()
|
||||||
|
|
||||||
|
return await $api(`/api/staff/time/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: { state: 'approved' },
|
body: {
|
||||||
|
state: 'approved',
|
||||||
|
//@ts-ignore
|
||||||
|
approved_by: auth.user.id,
|
||||||
|
approved_at: now,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,15 @@ const router = useRouter()
|
|||||||
const items = ref([])
|
const items = ref([])
|
||||||
const dataLoaded = ref(false)
|
const dataLoaded = ref(false)
|
||||||
|
|
||||||
|
const statementallocations = ref([])
|
||||||
|
const incominginvoices = ref([])
|
||||||
|
|
||||||
const setupPage = async () => {
|
const setupPage = async () => {
|
||||||
items.value = await useEntities("accounts").selectSpecial()
|
items.value = await useEntities("accounts").selectSpecial()
|
||||||
|
|
||||||
|
statementallocations.value = (await useEntities("statementallocations").select("*, bs_id(*)"))
|
||||||
|
incominginvoices.value = (await useEntities("incominginvoices").select("*, vendor(*)"))
|
||||||
|
|
||||||
items.value = await Promise.all(items.value.map(async (i) => {
|
items.value = await Promise.all(items.value.map(async (i) => {
|
||||||
let renderedAllocationsTemp = await renderedAllocations(i.id)
|
let renderedAllocationsTemp = await renderedAllocations(i.id)
|
||||||
let saldo = getSaldo(renderedAllocationsTemp)
|
let saldo = getSaldo(renderedAllocationsTemp)
|
||||||
@@ -37,22 +43,22 @@ const setupPage = async () => {
|
|||||||
|
|
||||||
const renderedAllocations = async (account) => {
|
const renderedAllocations = async (account) => {
|
||||||
|
|
||||||
let statementallocations = (await useEntities("statementallocations").select("*, bs_id(*)")).filter(i => i.account === account)
|
let statementallocationslocal = statementallocations.value.filter(i => i.account === account)
|
||||||
let incominginvoices = (await useEntities("incominginvoices").select("*, vendor(*)")).filter(i => i.accounts.find(x => x.account === account))
|
let incominginvoiceslocal = incominginvoices.value.filter(i => i.accounts.find(x => x.account === account))
|
||||||
|
|
||||||
let tempstatementallocations = statementallocations.map(i => {
|
let tempstatementallocations = statementallocationslocal.map(i => {
|
||||||
return {
|
return {
|
||||||
...i,
|
...i,
|
||||||
type: "statementallocation",
|
type: "statementallocation",
|
||||||
date: i.bs_id.date,
|
date: i.bankstatement.date,
|
||||||
partner: i.bs_id ? (i.bs_id.debName ? i.bs_id.debName : (i.bs_id.credName ? i.bs_id.credName : '')) : ''
|
partner: i.bankstatement ? (i.bankstatement.debName ? i.bankstatement.debName : (i.bankstatement.credName ? i.bankstatement.credName : '')) : ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
let incominginvoicesallocations = []
|
let incominginvoicesallocations = []
|
||||||
|
|
||||||
incominginvoices.forEach(i => {
|
incominginvoiceslocal.forEach(i => {
|
||||||
|
|
||||||
incominginvoicesallocations.push(...i.accounts.filter(x => x.account === account).map(x => {
|
incominginvoicesallocations.push(...i.accounts.filter(x => x.account === account).map(x => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ const setup = async () => {
|
|||||||
|
|
||||||
console.log(openDocuments.value)
|
console.log(openDocuments.value)
|
||||||
|
|
||||||
allocatedDocuments.value = documents.filter(i => i.statementallocations.find(x => x.bs_id === itemInfo.value.id))
|
allocatedDocuments.value = documents.filter(i => i.statementallocations.find(x => x.bankstatement === itemInfo.value.id))
|
||||||
allocatedIncomingInvoices.value = incominginvoices.filter(i => i.statementallocations.find(x => x.bs_id === itemInfo.value.id))
|
allocatedIncomingInvoices.value = incominginvoices.filter(i => i.statementallocations.find(x => x.bankstatement === itemInfo.value.id))
|
||||||
console.log(allocatedDocuments.value)
|
console.log(allocatedDocuments.value)
|
||||||
console.log(allocatedIncomingInvoices.value)
|
console.log(allocatedIncomingInvoices.value)
|
||||||
openIncomingInvoices.value = (await useEntities("incominginvoices").select("*, statementallocations(*), vendor(*)")).filter(i => !i.archived && i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== getInvoiceSum(i,false))
|
openIncomingInvoices.value = (await useEntities("incominginvoices").select("*, statementallocations(*), vendor(*)")).filter(i => !i.archived && i.statementallocations.reduce((n,{amount}) => n + amount, 0).toFixed(2) !== getInvoiceSum(i,false))
|
||||||
@@ -611,7 +611,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-check"
|
icon="i-heroicons-check"
|
||||||
:disabled="!accountToSave"
|
:disabled="!accountToSave"
|
||||||
@click="saveAllocation({bs_id: itemInfo.id, amount: manualAllocationSum, account: accountToSave, description: allocationDescription })"
|
@click="saveAllocation({bankstatement: itemInfo.id, amount: manualAllocationSum, account: accountToSave, description: allocationDescription })"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="accountToSave = ''"
|
@click="accountToSave = ''"
|
||||||
@@ -677,7 +677,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-check"
|
icon="i-heroicons-check"
|
||||||
:disabled="!ownAccountToSave"
|
:disabled="!ownAccountToSave"
|
||||||
@click="saveAllocation({bs_id: itemInfo.id, amount: manualAllocationSum, ownaccount: ownAccountToSave, description: allocationDescription })"
|
@click="saveAllocation({bankstatement: itemInfo.id, amount: manualAllocationSum, ownaccount: ownAccountToSave, description: allocationDescription })"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="accountToSave = ''"
|
@click="accountToSave = ''"
|
||||||
@@ -715,7 +715,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-check"
|
icon="i-heroicons-check"
|
||||||
:disabled="!customerAccountToSave"
|
:disabled="!customerAccountToSave"
|
||||||
@click="saveAllocation({bs_id: itemInfo.id, amount: manualAllocationSum, customer: customerAccountToSave, description: allocationDescription })"
|
@click="saveAllocation({bankstatement: itemInfo.id, amount: manualAllocationSum, customer: customerAccountToSave, description: allocationDescription })"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="customerAccountToSave = ''"
|
@click="customerAccountToSave = ''"
|
||||||
@@ -753,7 +753,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-check"
|
icon="i-heroicons-check"
|
||||||
:disabled="!vendorAccountToSave"
|
:disabled="!vendorAccountToSave"
|
||||||
@click="saveAllocation({bs_id: itemInfo.id, amount: manualAllocationSum, vendor: vendorAccountToSave, description: allocationDescription })"
|
@click="saveAllocation({bankstatement: itemInfo.id, amount: manualAllocationSum, vendor: vendorAccountToSave, description: allocationDescription })"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
@click="vendorAccountToSave = ''"
|
@click="vendorAccountToSave = ''"
|
||||||
@@ -811,7 +811,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
class="mr-3"
|
class="mr-3"
|
||||||
v-if="!itemInfo.statementallocations.find(i => i.cd_id === document.id)"
|
v-if="!itemInfo.statementallocations.find(i => i.cd_id === document.id)"
|
||||||
@click="saveAllocation({cd_id: document.id, bs_id: itemInfo.id, amount: Number(Number(document.openSum) < manualAllocationSum ? document.openSum : manualAllocationSum), description: allocationDescription})"
|
@click="saveAllocation({cd_id: document.id, bankstatement: itemInfo.id, amount: Number(Number(document.openSum) < manualAllocationSum ? document.openSum : manualAllocationSum), description: allocationDescription})"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
@@ -836,7 +836,7 @@ const archiveStatement = async () => {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
class="mr-3"
|
class="mr-3"
|
||||||
v-if="!itemInfo.statementallocations.find(i => i.ii_id === item.id)"
|
v-if="!itemInfo.statementallocations.find(i => i.ii_id === item.id)"
|
||||||
@click="saveAllocation({ii_id: item.id, bs_id: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(item,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(item,true)), description: allocationDescription})"
|
@click="saveAllocation({ii_id: item.id, bankstatement: itemInfo.id, amount: Number(Math.abs(getInvoiceSum(item,true)) > Math.abs(manualAllocationSum) ? manualAllocationSum : getInvoiceSum(item,true)), description: allocationDescription})"
|
||||||
/>
|
/>
|
||||||
<UButton
|
<UButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -26,6 +26,23 @@ const selectedUser = ref(platformIsNative ? auth.user.id : null)
|
|||||||
|
|
||||||
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
const canViewAll = computed(() => auth.permissions.includes('staff.time.read_all'))
|
||||||
|
|
||||||
|
const typeLabel = {
|
||||||
|
work: "Arbeitszeit",
|
||||||
|
vacation: "Urlaub",
|
||||||
|
sick: "Krankheit",
|
||||||
|
holiday: "Feiertag",
|
||||||
|
other: "Sonstiges"
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeColor = {
|
||||||
|
work: "gray",
|
||||||
|
vacation: "yellow",
|
||||||
|
sick: "rose",
|
||||||
|
holiday: "blue",
|
||||||
|
other: "gray"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function loadUsers() {
|
async function loadUsers() {
|
||||||
if (!canViewAll.value) return
|
if (!canViewAll.value) return
|
||||||
// Beispiel: User aus Supabase holen
|
// Beispiel: User aus Supabase holen
|
||||||
@@ -171,28 +188,65 @@ onMounted(async () => {
|
|||||||
{ key: 'started_at', label: 'Start' },
|
{ key: 'started_at', label: 'Start' },
|
||||||
{ key: 'stopped_at', label: 'Ende' },
|
{ key: 'stopped_at', label: 'Ende' },
|
||||||
{ key: 'duration_minutes', label: 'Dauer' },
|
{ key: 'duration_minutes', label: 'Dauer' },
|
||||||
|
{ key: 'user', label: 'Mitarbeiter' },
|
||||||
|
{ key: 'type', label: 'Typ' },
|
||||||
{ key: 'description', label: 'Beschreibung' },
|
{ key: 'description', label: 'Beschreibung' },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<template #state-data="{ row }">
|
<template #state-data="{ row }">
|
||||||
<span v-if="row.state === 'approved'" class="text-primary-500">Genehmigt</span>
|
<span v-if="row.state === 'approved'" class="text-primary-500">Genehmigt</span>
|
||||||
<span v-else-if="row.state === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
<span v-else-if="row.state === 'submitted'" class="text-cyan-500">Eingereicht</span>
|
||||||
<span v-else-if="row.state === 'draft'" class="text-red-500">Entwurf</span>
|
<span v-else-if="row.state === 'draft'" class="text-red-500">Entwurf</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template #type-data="{ row }">
|
||||||
<template #started_at-data="{ row }">
|
<UBadge :color="typeColor[row.type] || 'gray'">
|
||||||
{{ useNuxtApp().$dayjs(row.started_at).format("DD.MM.YY HH:mm") }}
|
{{ typeLabel[row.type] || row.type }}
|
||||||
|
</UBadge>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- START -->
|
||||||
|
<template #started_at-data="{ row }">
|
||||||
|
<!-- Urlaub / Krankheit → nur Tag -->
|
||||||
|
<span v-if="row.type === 'vacation' || row.type === 'sick'">
|
||||||
|
{{ useNuxtApp().$dayjs(row.started_at).format("DD.MM.YY") }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Arbeitszeit / andere → Datum + Uhrzeit -->
|
||||||
|
<span v-else>
|
||||||
|
{{ useNuxtApp().$dayjs(row.started_at).format("DD.MM.YY HH:mm") }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ENDE -->
|
||||||
<template #stopped_at-data="{ row }">
|
<template #stopped_at-data="{ row }">
|
||||||
<span v-if="row.stopped_at">
|
<span v-if="!row.stopped_at" class="text-primary-500 font-medium">
|
||||||
|
läuft...
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Urlaub / Krankheit → nur Tag -->
|
||||||
|
<span v-else-if="row.type === 'vacation' || row.type === 'sick'">
|
||||||
|
{{ useNuxtApp().$dayjs(row.stopped_at).format("DD.MM.YY") }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Arbeitszeit / andere → Datum + Uhrzeit -->
|
||||||
|
<span v-else>
|
||||||
{{ useNuxtApp().$dayjs(row.stopped_at).format("DD.MM.YY HH:mm") }}
|
{{ useNuxtApp().$dayjs(row.stopped_at).format("DD.MM.YY HH:mm") }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-primary-500 font-medium">läuft...</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #duration_minutes-data="{ row }">
|
<template #duration_minutes-data="{ row }">
|
||||||
{{ row.duration_minutes ? useFormatDuration(row.duration_minutes) : "-" }}
|
|
||||||
|
<!-- Urlaub / Krankheit → Tage anzeigen -->
|
||||||
|
<span v-if="row.type === 'vacation' || row.type === 'sick'">
|
||||||
|
<!-- {{ useFormatDurationDays(row.startet_at, row.stopped_at) }}-->--
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Arbeitszeit / andere → Minutenformat -->
|
||||||
|
<span v-else>
|
||||||
|
{{ row.duration_minutes ? useFormatDuration(row.duration_minutes) : "-" }}
|
||||||
|
</span>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #actions-data="{ row }">
|
<template #actions-data="{ row }">
|
||||||
@@ -218,6 +272,14 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
</template>
|
</template>
|
||||||
|
<template #user-data="{ row }">
|
||||||
|
{{users.find(i => i.user_id === row.user_id) ? users.find(i => i.user_id === row.user_id).full_name : ""}}
|
||||||
|
</template>
|
||||||
|
<template #description-data="{ row }">
|
||||||
|
<span v-if="row.type === 'vacation'">{{row.vacation_reason}}</span>
|
||||||
|
<span v-else-if="row.type === 'sick'">{{row.sick_reason}}</span>
|
||||||
|
<span v-else>{{row.description}}</span>
|
||||||
|
</template>
|
||||||
</UTable>
|
</UTable>
|
||||||
</UDashboardPanelContent>
|
</UDashboardPanelContent>
|
||||||
</template>
|
</template>
|
||||||
@@ -284,9 +346,16 @@ onMounted(async () => {
|
|||||||
@click="handleEdit(row)"
|
@click="handleEdit(row)"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<p class="font-semibold">
|
<div class="font-semibold flex items-center gap-2">
|
||||||
{{ row.description || 'Keine Beschreibung' }}
|
<span>{{ row.description || 'Keine Beschreibung' }}</span>
|
||||||
</p>
|
|
||||||
|
<UBadge
|
||||||
|
:color="typeColor[row.type]"
|
||||||
|
class="text-xs"
|
||||||
|
>
|
||||||
|
{{ typeLabel[row.type] }}
|
||||||
|
</UBadge>
|
||||||
|
</div>
|
||||||
|
|
||||||
<UBadge
|
<UBadge
|
||||||
:color="{
|
:color="{
|
||||||
@@ -359,5 +428,12 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- MODAL -->
|
<!-- MODAL -->
|
||||||
<StaffTimeEntryModal v-model="showModal" :entry="editEntry" @saved="load" />
|
<StaffTimeEntryModal
|
||||||
|
v-model="showModal"
|
||||||
|
:entry="editEntry"
|
||||||
|
@saved="load"
|
||||||
|
:users="users"
|
||||||
|
:can-select-user="canViewAll"
|
||||||
|
:default-user-id="selectedUser"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user