Managing own invoices

This commit is contained in:
2026-02-13 12:38:48 +01:00
parent ab711109a7
commit f468814a2f
15 changed files with 715 additions and 21 deletions

View File

@@ -21,7 +21,10 @@ class DashboardController extends CommonController {
private function renderForLoggedInUser(Request $request) { private function renderForLoggedInUser(Request $request) {
$authCheckProvider = new AuthCheckProvider; $authCheckProvider = new AuthCheckProvider;
$inertiaProvider = new InertiaProvider('Dashboard/Dashboard', ['appName' => app('tenant')->name]); $inertiaProvider = new InertiaProvider('Dashboard/Dashboard', [
'myInvoices' => $this->invoices->getMyInvoicesWidget()
]);
return $inertiaProvider->render(); return $inertiaProvider->render();
} }

View File

@@ -1,8 +1,17 @@
<script setup> <script setup>
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue' import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue"; import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
import {onMounted} from "vue"; import MyInvoices from "./Partials/Widgets/MyInvoices.vue";
import {toast} from "vue3-toastify";
const props = defineProps({
myInvoices: Object,
})
function newInvoice() {
window.location.href = '/invoice/new';
}
</script> </script>
<template> <template>
@@ -13,7 +22,10 @@ import {toast} from "vue3-toastify";
</shadowed-box> </shadowed-box>
<shadowed-box class="dashboard-widget-box"> <shadowed-box class="dashboard-widget-box">
Meine Abrechnungen <MyInvoices />
<input type="button" value="Neue Abrechnung" @click="newInvoice" style="margin-top: 20px;">
</shadowed-box> </shadowed-box>
</diV> </diV>
</AppLayout> </AppLayout>
@@ -31,7 +43,6 @@ import {toast} from "vue3-toastify";
.dashboard-widget-box { .dashboard-widget-box {
flex-grow: 1; display: inline-block; flex-grow: 1; display: inline-block;
height: 150px; margin: 0 10px;
margin: 0 10px;
} }
</style> </style>

View File

@@ -8,7 +8,7 @@ const myInvoices = reactive({
}) })
onMounted(async () => { onMounted(async () => {
const response = await fetch('/api/v1/dashboard/my-invoices'); const response = await fetch('/api/v1/dashboard/my-invoices');
const data = await response.json(); const data = await response.json();
Object.assign(myInvoices, data); Object.assign(myInvoices, data);
}); });
@@ -17,7 +17,7 @@ onMounted(async () => {
<template> <template>
<p v-for="invoice in myInvoices.myInvoices" class="widget-content-item"> <p v-for="invoice in myInvoices.myInvoices" class="widget-content-item">
<a :href="'/invoices/my-invoices/' + invoice.slug" class="link">{{invoice.title}} ({{invoice.count}})</a> <a :href="'/invoice/my-invoices/' + invoice.slug" class="link">{{invoice.title}} ({{invoice.count}})</a>
<label> <label>
{{invoice.amount}} {{invoice.amount}}
</label> </label>

View File

@@ -36,9 +36,9 @@ class ChangeStatusCommand {
$this->request->invoice->comment = $this->request->invoice->denied_reason; $this->request->invoice->comment = $this->request->invoice->denied_reason;
$this->request->invoice->denied_reason = null; $this->request->invoice->denied_reason = null;
break; break;
case InvoiceStatus::INVOICE_STATUS_DELETED:
$this->request->invoice->status = InvoiceStatus::INVOICE_STATUS_DELETED;
break;
} }
if ($this->request->invoice->save()) { if ($this->request->invoice->save()) {

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Domains\Invoice\Controllers;
use App\Enumerations\InvoiceStatus;
use App\Providers\InertiaProvider;
use App\Scopes\CommonController;
use Illuminate\Http\JsonResponse;
class ListMyInvoicesController extends CommonController {
function __invoke(string $invoiceStatus) {
$invoices = $this->invoices->getMyInvoicesByStatus($invoiceStatus);
$subTabIndex = 0;
switch ($invoiceStatus) {
case InvoiceStatus::INVOICE_STATUS_NEW:
$subTabIndex = 0;
break;
case InvoiceStatus::INVOICE_STATUS_APPROVED:
$subTabIndex = 1;
break;
case InvoiceStatus::INVOICE_STATUS_DENIED:
$subTabIndex = 2;
break;
}
$inertiaProvider = new InertiaProvider('Invoice/ListMyInvoices', [
'invoices' => $invoices,
'endpoint' => $invoiceStatus,
'currentStatus' => $subTabIndex,
]);
return $inertiaProvider->render();
}
public function getMyInvoicesByStatus(string $invoiceStatus) : JsonResponse {
$invoices = $this->invoices->getMyInvoicesByStatus($invoiceStatus);
$title = '';
switch ($invoiceStatus) {
case InvoiceStatus::INVOICE_STATUS_NEW:
$title = 'Neue Abrechnungen';
break;
case InvoiceStatus::INVOICE_STATUS_APPROVED:
$title = 'Freigegebene Abrechnungen, nicht exportierte Abrechnungen';
break;
case InvoiceStatus::INVOICE_STATUS_DENIED:
$title = 'Abgelehnte Abrechnungen';
break;
}
return response()->json([
'title' => $title,
'endpoint' => $invoiceStatus,
'invoices' => $invoices,
]);
}
}

View File

@@ -4,6 +4,7 @@ use App\Domains\CostUnit\Controllers\DistanceAllowanceController;
use App\Domains\CostUnit\Controllers\ListController; use App\Domains\CostUnit\Controllers\ListController;
use App\Domains\Invoice\Controllers\ChangeStateController; use App\Domains\Invoice\Controllers\ChangeStateController;
use App\Domains\Invoice\Controllers\EditController; use App\Domains\Invoice\Controllers\EditController;
use App\Domains\Invoice\Controllers\ListMyInvoicesController;
use App\Domains\Invoice\Controllers\NewInvoiceController; use App\Domains\Invoice\Controllers\NewInvoiceController;
use App\Domains\Invoice\Controllers\ShowInvoiceController; use App\Domains\Invoice\Controllers\ShowInvoiceController;
use App\Middleware\IdentifyTenant; use App\Middleware\IdentifyTenant;
@@ -19,6 +20,7 @@ Route::middleware(IdentifyTenant::class)->group(function () {
Route::post('/details/{invoiceId}/change-state/{newState}', ChangeStateController::class); Route::post('/details/{invoiceId}/change-state/{newState}', ChangeStateController::class);
Route::post('/details/{invoiceId}/copy', [EditController::class, 'copyInvoice']); Route::post('/details/{invoiceId}/copy', [EditController::class, 'copyInvoice']);
Route::post('/details/{invoiceId}/update', [EditController::class, 'updateInvoice']); Route::post('/details/{invoiceId}/update', [EditController::class, 'updateInvoice']);
Route::get('/my-invoices/{invoiceStatus}', [ListMyInvoicesController::class, 'getMyInvoicesByStatus']);

View File

@@ -1,7 +1,6 @@
<?php <?php
use App\Domains\CostUnit\Controllers\OpenController;
use App\Domains\CostUnit\Controllers\CreateController; use App\Domains\Invoice\Controllers\ListMyInvoicesController;
use App\Domains\CostUnit\Controllers\ListController;
use App\Domains\Invoice\Controllers\NewInvoiceController; use App\Domains\Invoice\Controllers\NewInvoiceController;
use App\Middleware\IdentifyTenant; use App\Middleware\IdentifyTenant;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@@ -10,10 +9,9 @@ Route::middleware(IdentifyTenant::class)->group(function () {
Route::prefix('invoice')->group(function () { Route::prefix('invoice')->group(function () {
Route::get('/new', NewInvoiceController::class); Route::get('/new', NewInvoiceController::class);
Route::middleware(['auth'])->group(function () { Route::middleware(['auth'])->group(function () {
Route::get('/create', [CreateController::class, 'showForm']); Route::get('/my-invoices/{invoiceStatus}', ListMyInvoicesController::class);
}); });

View File

@@ -0,0 +1,58 @@
<script setup>
import {reactive, inject, onMounted} from 'vue';
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue';
import { useAjax } from "../../../../resources/js/components/ajaxHandler.js";
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
import {toast} from "vue3-toastify";
import ListInvoices from "./Partials/myInvoiceDetails/ListInvoices.vue";
const props = defineProps({
currentStatus: Number,
})
console.log(props.currentStatus)
const initialCostUnitId = props.cost_unit_id
const initialInvoiceId = props.invoice_id
const tabs = [
{
title: 'Neue Abrechnungen',
component: ListInvoices,
endpoint: "/api/v1/invoice/my-invoices/new",
deep_jump_id: initialCostUnitId,
deep_jump_id_sub: initialInvoiceId,
},
{
title: 'Freigegebene Abrechnungen',
component: ListInvoices,
endpoint: "/api/v1/invoice/my-invoices/approved",
deep_jump_id: initialCostUnitId,
deep_jump_id_sub: initialInvoiceId,
},
{
title: 'Abgelehnte Abrechnungen',
component: ListInvoices,
endpoint: "/api/v1/invoice/my-invoices/denied",
deep_jump_id: initialCostUnitId,
deep_jump_id_sub: initialInvoiceId,
},
]
onMounted(() => {
if (undefined !== props.message) {
toast.success(props.message)
}
})
</script>
<template>
<AppLayout title="Meine Abrechnungen">
<shadowed-box style="width: 95%; margin: 20px auto; padding: 20px; overflow-x: hidden;">
<tabbed-page :tabs="tabs" :subTabIndex=props.currentStatus />
</shadowed-box>
</AppLayout>
</template>

View File

@@ -0,0 +1,78 @@
<script setup>
const props = defineProps({
invoice: {
type: Object,
default: () => ({})
}
})
</script>
<template>
<table class="travel_allowance">
<tr><td colspan="2">
Abrechnung einer Reisekostenpauschale
</td></tr>
<tr>
<th>Reiseroute</th>
<td>{{props.invoice.travelRoute}}</td>
</tr>
<tr>
<th>Gesamte Wegstrecke</th>
<td>{{props.invoice.distance}} km</td>
</tr>
<tr>
<th>Kilometerpauschale</th>
<td>{{props.invoice.distanceAllowance}} Euro / km</td>
</tr>
<tr>
<th>Gesamtbetrag</th>
<td style="font-weight: bold">{{props.invoice.amount}}</td>
</tr>
<tr>
<th>Marterialtransport</th>
<td>{{props.invoice.transportation}}</td>
</tr>
<tr>
<th>Hat Personen mitgenommen</th>
<td>{{props.invoice.passengers}}</td>
</tr>
</table>
</template>
<style scoped>
.travel_allowance {
border-spacing: 0;
}
.travel_allowance tr th {
width: 300px !important;
border-left: 1px solid #ccc;
padding-left: 20px;
}
.travel_allowance tr td,
.travel_allowance tr th {
font-family: sans-serif;
line-height: 1.8em;
border-bottom: 1px solid #ccc;
padding: 10px;
}
.travel_allowance tr td:last-child {
border-right: 1px solid #ccc;
}
.travel_allowance tr:first-child td:first-child {
margin-bottom: 10px;
font-weight: bold;
background: linear-gradient(to bottom, #fff, #f6f7f7);
border-top: 1px solid #ccc;
border-left: 1px solid #ccc !important;
}
</style>

View File

@@ -0,0 +1,100 @@
<script setup>
const props = defineProps({
data: {
type: Object,
required: true
},
modeShow: {
type: Boolean,
required: true,
}
})
const emit = defineEmits(["accept", "deny", "fix", "reopen"])
</script>
<template>
<span id="invoice_details_header">
<table>
<tr>
<td>Name:</td>
<td v-if="modeShow">{{props.data.contactName}}</td>
<td v-else style="width: 300px;">{{props.data.contactName}}</td>
<td v-if="modeShow" style="width: 250px;">Kostenstelle</td>
<td v-else style="width: 300px;">Kostensatelle (ursprünglich)</td>
<td>{{props.data.costUnitName}}</td>
<td rowspan="4" style="vertical-align: top;">
<button
v-if="props.data.status === 'new' && modeShow"
@click="emit('reject')"
class="button mareike-button mareike-deny-button"
>
Abrechnung zurückziehen
</button>
<button v-if="props.data.status === 'denied' && modeShow"
@click="emit('delete')"
class="button mareike-button mareike-deny-button"
>
Abrechnung Endgültig löschen
</button>
</td>
</tr>
<!-- Rest der Tabelle bleibt unverändert -->
<tr>
<td>E-Mail:</td>
<td>{{props.data.contactEmail}}</td>
<td>
Abrechnungsnummer
<label v-if="!modeShow"> (ursprünglich)</label>:
</td>
<td>{{props.data.invoiceNumber}}</td>
</tr>
<tr>
<td>Telefon:</td>
<td>{{props.data.contactPhone}}</td>
<td>
Abrechnungstyp
<label v-if="!modeShow"> (Ursprünglich)</label>:
</td>
<td>{{props.data.invoiceType}}</td>
</tr>
<tr>
<td>Kontoinhaber*in:</td>
<td>{{props.data.accountOwner}}</td>
<td>Gesamtbetrag
<label v-if="!modeShow"> (Ursprünglich)</label>:
</td>
<td><strong>{{props.data.amount}}</strong></td>
</tr>
<tr>
<td>IBAN:</td>
<td>{{props.data.accountIban}}</td>
<td>Buchungsinformationen:</td>
<td v-if="props.data.donation">Als Spende gebucht</td>
<td v-else-if="props.data.alreadyPaid">Beleg ohne Auszahlung</td>
<td v-else>Klassische Auszahlung</td>
</tr>
<tr>
<td>Status:</td>
<td>{{props.data.readableStatus}}</td>
<td>Anmerkungen:</td>
<td>
<span v-if="props.data.status === 'denied'">
{{props.data.deniedReason}}
</span>
<span v-else>{{props.data.comment}}</span>
</td>
</tr>
</table>
</span>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,264 @@
<script setup>
import FullScreenModal from "../../../../../Views/Components/FullScreenModal.vue";
import {ref} from "vue";
import Modal from "../../../../../Views/Components/Modal.vue";
import { useAjax } from "../../../../../../resources/js/components/ajaxHandler.js";
import ShowInvoicePartial from "./ShowInvoice.vue";
import Header from "./Header.vue";
import {toast} from "vue3-toastify";
const props = defineProps({
data: {
type: Object,
default: () => ({})
}, showInvoice: Boolean
})
const showInvoice = ref(props.showInvoice)
const emit = defineEmits(["close"])
const denyInvoiceDialog = ref(false)
const { data, loading, error, request } = useAjax()
const modeShow = ref(true)
const costUnits = ref(null)
const newInvoice = ref(null)
async function acceptInvoice() {
const data = await request("/api/v1/invoice/details/" + props.data.id + "/change-state/approved", {
method: "POST",
});
if (data.status === 'success') {
toast.success('Abrechnung wurde freigegeben.');
} else {
toast.error('Bei der Bearbeitung ist ein Fehler aufgetreten.');
}
close();
}
function close() {
emit('reload')
emit('close')
}
async function updateInvoice(formData) {
const data = await request("/api/v1/invoice/details/" + props.data.id + "/update", {
method: "POST",
body: {
invoiceData: formData
}
});
if (!data.do_copy) {
modeShow.value = true;
toast.success('Die Koreektur der Abrechnung wurde gespeichert.');
close();
} else {
modeShow.value = true;
newInvoice.value = data.invoice;
props.data.id = data.invoice.id;
reloadInvoiceFixDialog()
}
}
async function reloadInvoiceFixDialog() {
const data = await request("api/v1/invoice/details/" + props.data.id, {
method: "GET",
});
newInvoice.value = data.invoice;
props.data.id = data.invoice.id;
costUnits.value = data.costUnits;
props.data.id = data.invoice.id;
modeShow.value = false;
toast.success('Die Abrechnung wurde gespeichert und eine neue Abrechnung wurde erstellt.');
}
async function openInvoiceFixDialog() {
const data = await request("/api/v1/invoice/details/" + props.data.id + "/copy", {
method: "POST",
body: {
}
});
if (data.status === 'success') {
costUnits.value = data.costUnits;
newInvoice.value = data.invoice;
props.data.id = data.invoice.id;
modeShow.value = false;
}
}
function openDenyInvoiceDialog() {
denyInvoiceDialog.value = true;
}
async function rejectInvoice() {
const data = await request("/api/v1/invoice/details/" + props.data.id + "/change-state/denied", {
method: "POST",
body: {
reason: 'Von Antragssteller*in zurückgezogen'
}
});
if (data.status === 'success') {
toast.success('Abrechnung wurde zurückgezogen.');
} else {
toast.error('Bei der Bearbeitung ist ein Fehler aufgetreten.');
}
denyInvoiceDialog.value = false;
close();
}
async function deleteInvoice() {
const data = await request("/api/v1/invoice/details/" + props.data.id + "/change-state/deleted", {
method: "POST",
});
if (data.status === 'success') {
toast.success('Die Abrechnung wurde gelöscht.');
} else {
toast.error('Beim Bearbeiten ist ein Fehler aufgetreten.');
}
close();
}
</script>
<template>
<FullScreenModal
:show="showInvoice"
title="Abrechnungsdetails"
@close="emit('close')"
>
<Header :data="props.data"
@reject="rejectInvoice"
@delete="deleteInvoice"
:modeShow="modeShow"
/>
<ShowInvoicePartial :data="props.data" />
</FullScreenModal>
<Modal title="Abrechnung ablehnen" :show="denyInvoiceDialog" @close="denyInvoiceDialog = false" >
Begründung:
<textarea class="mareike-textarea" style="width: 100%; height: 100px; margin-top: 10px;" id="deny_invoice_reason" />
<input type="button" class="mareike-button mareike-deny-invoice-button" value="Abrechnung ablehnen" @click="denyInvoice" />
</Modal>
</template>
<style>
.mareike-deny-invoice-button {
width: 150px !important;
margin-top: 10px;
}
#invoice_details_header{
font-weight: bold;
font-size: 12pt;
line-height: 1.8em;
width: 100%;
}
#invoice_details_header table {
border-style: solid;
border-width: 1px;
border-radius: 10px !important;
width: 98%;
border-color: #c0c0c0;
box-shadow: 5px 5px 10px #c0c0c0;
margin-bottom: 75px;
font-weight: normal;
}
#invoice_details_header table tr td:first-child {
padding-right: 50px;
width: 175px;
}
#invoice_details_header table tr td:nth-child(2) {
padding-right: 25px;
}
#invoice_details_header table tr td:nth-child(3) {
padding-right: 50px;
width: 100px;
vertical-align: top;
}
#invoice_details_body {
height: 400px;
overflow: auto;
}
#invoice_details_body table tr:nth-child(1) td,
#invoice_details_body table tr:nth-child(2) td,
#invoice_details_body table tr:nth-child(3) td,
#invoice_details_body table tr:nth-child(4) td,
#invoice_details_body table tr:nth-child(6) td{
vertical-align: top;
height: 20px;
}
#invoice_details_body table tr:nth-child(5) td {
height: 50px;
}
#invoice_details_body table {
width: 100%;
}
#invoice_details_body table tr:nth-child(1) td:first-child {
padding-right: 50px;
}
#invoice_details_body table tr:nth-child(1) td:nth-child(2),
#invoice_details_body table tr:nth-child(1) td:nth-child(3)
{
width: 250px;
}
.mareike-accept-button {
background-color: #36c054 !important;
color: #ffffff !important;
}
.mareike-deny-button {
background-color: #ee4b5c !important;
color: #ffffff !important;
}
.mareike-fix-button {
background-color: #d3d669 !important;
color: #67683c !important;
}
.mareike-button {
padding: 5px 25px !important;
font-size: 11pt !important;
margin-bottom: 10px;
width: 100%;
padding-right: 2px;
}
</style>

View File

@@ -0,0 +1,80 @@
<script setup>
import Icon from "../../../../../Views/Components/Icon.vue";
import InvoiceDetails from "./InvoiceDetails.vue";
import { useAjax } from "../../../../../../resources/js/components/ajaxHandler.js";
import {ref} from "vue";
const props = defineProps({
data: Object
})
const { request } = useAjax()
const invoice = ref(null)
const show_invoice = ref(false)
const localData = ref(props.data)
async function openInvoiceDetails(invoiceId) {
const url = '/api/v1/invoice/details/' + invoiceId
try {
const response = await fetch(url, { method: 'GET' })
const result = await response.json()
invoice.value = result.invoice
show_invoice.value = true
} catch (err) {
console.error('Error fetching invoices:', err)
}
}
async function reload() {
const url = "/api/v1/invoice/my-invoices/" + props.data.endpoint
try {
const response = await fetch(url, { method: 'GET' })
if (!response.ok) throw new Error('Fehler beim Laden')
const result = await response.json()
localData.value = result
} catch (err) {
console.error('Error fetching invoices:', err)
}
}
</script>
<template>
<table v-if="localData.invoices.length > 0" class="invoice-list-table">
<tr>
<td colspan="6">{{props.data.title}}</td>
</tr>
<tr v-for="invoice in localData.invoices" :id="'invoice_' + invoice.id">
<td>{{invoice.invoiceNumber}}</td>
<td>{{invoice.invoiceType}}</td>
<td>
{{invoice.amount}}
</td>
<td style="width: 150px;">
<Icon v-if="invoice.donation" name="hand-holding-dollar" style="color: #ffffff; background-color: green" />
<Icon v-if="invoice.alreadyPaid" name="comments-dollar" style="color: #ffffff; background-color: green" />
</td>
<td>
{{invoice.contactName}}<br />
<label v-if="invoice.contactEmail !== '--'">{{invoice.contactEmail}}<br /></label>
<label v-if="invoice.contactPhone !== '--'">{{invoice.contactPhone}}<br /></label>
</td>
<td>
<input type="button" value="Abrechnung Anzeigen" @click="openInvoiceDetails(invoice.id)" />
</td>
</tr>
</table>
<p v-else>Es sind keine Abrechnungen in dieser Kategorie vorhanden.</p>
<InvoiceDetails :data="invoice" :show-invoice="show_invoice" v-if="show_invoice" @close="show_invoice = false; reload()" />
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,19 @@
<script setup>
import PdfViewer from "../../../../../Views/Components/PdfViewer.vue";
import DistanceAllowance from "./DistanceAllowance.vue";
const props = defineProps({
data: {
type: Object,
required: true
}
})
</script>
<template>
<span id="invoice_details_body">
<PdfViewer :url="'/api/v1/invoice/showReceipt/' + props.data.id" v-if="props.data.documentFilename !== null" />
<DistanceAllowance v-else :invoice="props.data" />
</span>
</template>

View File

@@ -13,9 +13,9 @@ use Illuminate\Database\Eloquent\Collection;
class InvoiceRepository { class InvoiceRepository {
public function getMyInvoicesWidget() : array { public function getMyInvoicesWidget() : array {
$invoices = [ $invoices = [
InvoiceStatus::INVOICE_STATUS_NEW => ['slug' => 'new', 'title' => 'Neue Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()], InvoiceStatus::INVOICE_STATUS_NEW => ['slug' => InvoiceStatus::INVOICE_STATUS_NEW, 'title' => 'Neue Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()],
InvoiceStatus::INVOICE_STATUS_APPROVED => ['slug' => 'approved', 'title' => 'Freigegebene Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()], InvoiceStatus::INVOICE_STATUS_APPROVED => ['slug' => InvoiceStatus::INVOICE_STATUS_APPROVED, 'title' => 'Freigegebene Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()],
InvoiceStatus::INVOICE_STATUS_DENIED => ['slug' => 'declined', 'title' => 'Abgelehnte Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()] InvoiceStatus::INVOICE_STATUS_DENIED => ['slug' => InvoiceStatus::INVOICE_STATUS_DENIED, 'title' => 'Abgelehnte Abrechnungen', 'count' => 0, 'amount' => Amount::fromString('0')->toString()]
]; ];
@@ -48,6 +48,21 @@ class InvoiceRepository {
return $returnData; return $returnData;
} }
public function getMyInvoicesByStatus(string $status) : array {
$returnData = [];
foreach (Invoice::where(
[
'status' => $status,
'user_id' => auth()->user()->id,
'tenant' => app('tenant')->slug,
]
)->get() as $invoice) {
$returnData[] = new InvoiceResource($invoice)->toArray();
};
return $returnData;
}
public function getAsTreasurer(int $invoiceId) : ?Invoice { public function getAsTreasurer(int $invoiceId) : ?Invoice {
$invoice = Invoice::where('id', $invoiceId)->first(); $invoice = Invoice::where('id', $invoiceId)->first();
if ($invoice === null) { if ($invoice === null) {

View File

@@ -6,6 +6,11 @@ const props = defineProps({
type: Array, type: Array,
required: true, required: true,
// [{ title: "Titel", component: Component, endpoint: "/wp-json/..." }] // [{ title: "Titel", component: Component, endpoint: "/wp-json/..." }]
},
subTabIndex: {
type: Number,
required: false,
default: 0
} }
}) })
@@ -54,7 +59,7 @@ async function selectTab(index) {
// ersten Tab automatisch laden // ersten Tab automatisch laden
onMounted(() => { onMounted(() => {
if (props.tabs.length > 0) { if (props.tabs.length > 0) {
selectTab(0) selectTab(props.subTabIndex)
} }
}) })
</script> </script>