Operation processes on invoices
This commit is contained in:
@@ -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>
|
||||
@@ -0,0 +1,116 @@
|
||||
<script setup>
|
||||
import PdfViewer from "../../../../../Views/Components/PdfViewer.vue";
|
||||
import DistanceAllowance from "./DistanceAllowance.vue";
|
||||
import {onMounted, reactive} from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
newInvoice: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
costUnits: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['submit', 'cancel'])
|
||||
|
||||
const formData = reactive({
|
||||
type_internal: props.newInvoice.internalType || '',
|
||||
cost_unit: props.newInvoice.costUnitId || '',
|
||||
amount: props.newInvoice.amountPlain || '',
|
||||
reason_of_correction: '',
|
||||
})
|
||||
|
||||
const submitForm = () => {
|
||||
emit('submit', formData)
|
||||
}
|
||||
|
||||
|
||||
const invoiceTypeCollection = reactive({
|
||||
invoiceTypes: {}
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
const response = await fetch('/api/v1/core/retrieve-invoice-types-all');
|
||||
const data = await response.json();
|
||||
Object.assign(invoiceTypeCollection, data);
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="submitForm">
|
||||
<table style="width: 100%; font-family: sans-serif;">
|
||||
<tr>
|
||||
<td style="width: 150px;">Rechnungstyp:</td>
|
||||
<td style="width: 450px;">
|
||||
<select v-model="formData.type_internal" class="width-half-full">
|
||||
<option v-for="invoiceType in invoiceTypeCollection.invoiceTypes" :value="invoiceType.slug">{{invoiceType.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Kostenstelle:</td>
|
||||
<td>
|
||||
<select v-model="formData.cost_unit" class="width-half-full">
|
||||
<option v-for="costUnit in props.costUnits" :value="costUnit.id">{{costUnit.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Gesamtbetrag:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.amount" class="width-small" /> Euro
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Grund der Korrektur:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.reason_of_correction" class="width-half-full" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="checkbox" v-model="formData.duplicate" id="mareike_correct_invoice_duplicate" />
|
||||
<label for="mareike_correct_invoice_duplicate">Kopie zur Weiterbearbeitung erstellen</label>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="submit" value="Speichern und freigeben" class="button mareike-accept-button" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
<br /><br />
|
||||
|
||||
<PdfViewer :url="'/api/v1/invoice/showReceipt/' + props.newInvoice.id" v-if="props.newInvoice.documentFilename !== null" />
|
||||
<DistanceAllowance v-else :invoice="props.newInvoice" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.width-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width-almost-full {
|
||||
width: calc(100% - 75px);
|
||||
}
|
||||
|
||||
.width-half-full {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.width-small {
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
117
app/Domains/Invoice/Views/Partials/invoiceDetails/Header.vue
Normal file
117
app/Domains/Invoice/Views/Partials/invoiceDetails/Header.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<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">
|
||||
<button
|
||||
v-if="props.data.status === 'new' && modeShow"
|
||||
@click="emit('accept')"
|
||||
class="button mareike-button mareike-accept-button"
|
||||
>
|
||||
Abrechnung annehmen
|
||||
</button>
|
||||
<button v-if="props.data.status === 'denied' && modeShow"
|
||||
@click="emit('reopen')"
|
||||
class="button mareike-button mareike-accept-button"
|
||||
>
|
||||
Abrechnung zur Wiedervorlage öffnen
|
||||
</button>
|
||||
<br />
|
||||
|
||||
<button
|
||||
v-if="props.data.status === 'new' && modeShow"
|
||||
@click="emit('fix')"
|
||||
class="button mareike-button mareike-fix-button"
|
||||
>
|
||||
Abrechnung ablehnen und korrigieren
|
||||
</button><br />
|
||||
|
||||
<button
|
||||
v-if="props.data.status === 'new' && modeShow"
|
||||
@click="emit('deny')"
|
||||
class="button mareike-button mareike-deny-button"
|
||||
>
|
||||
Abrechnung ablehnen
|
||||
</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>
|
||||
@@ -0,0 +1,285 @@
|
||||
<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 EditInvoicePartial from "./EditInvoice.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 denyInvoice() {
|
||||
const data = await request("/api/v1/invoice/details/" + props.data.id + "/change-state/denied", {
|
||||
method: "POST",
|
||||
body: {
|
||||
reason: document.getElementById('deny_invoice_reason').value
|
||||
}
|
||||
});
|
||||
|
||||
if (data.status === 'success') {
|
||||
toast.success('Abrechnung wurde abgelehnt.');
|
||||
} else {
|
||||
toast.error('Bei der Bearbeitung ist ein Fehler aufgetreten.');
|
||||
|
||||
}
|
||||
|
||||
denyInvoiceDialog.value = false;
|
||||
close();
|
||||
}
|
||||
|
||||
async function reopenInvoice() {
|
||||
const data = await request("/api/v1/invoice/details/" + props.data.id + "/change-state/new", {
|
||||
method: "POST",
|
||||
|
||||
});
|
||||
|
||||
if (data.status === 'success') {
|
||||
toast.success('Die Abrechnung wurde zur erneuten Bearbeitung vorgelegt');
|
||||
} else {
|
||||
toast.error('Beim Bearbeiten ist ein Fehler aufgetreten.');
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<FullScreenModal
|
||||
:show="showInvoice"
|
||||
title="Abrechnungsdetails"
|
||||
@close="emit('close')"
|
||||
>
|
||||
|
||||
<Header :data="props.data"
|
||||
@accept="acceptInvoice"
|
||||
@deny="openDenyInvoiceDialog"
|
||||
@fix="openInvoiceFixDialog"
|
||||
@reopen="reopenInvoice"
|
||||
:modeShow="modeShow"
|
||||
/>
|
||||
|
||||
<ShowInvoicePartial
|
||||
v-if="modeShow"
|
||||
:data="props.data"
|
||||
@accept="acceptInvoice"
|
||||
@deny="openDenyInvoiceDialog"
|
||||
@fix="openInvoiceFixDialog"
|
||||
/>
|
||||
|
||||
<EditInvoicePartial
|
||||
v-else
|
||||
:newInvoice="newInvoice"
|
||||
:costUnits="costUnits"
|
||||
@accept="acceptInvoice"
|
||||
@deny="openDenyInvoiceDialog"
|
||||
@fix="openInvoiceFixDialog"
|
||||
@update="updateInvoice"
|
||||
@submit="updateInvoice"
|
||||
/>
|
||||
|
||||
|
||||
</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>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import PdfViewer from "../../../../../Views/Components/PdfViewer.vue";
|
||||
import DistanceAllowance from "./DistanceAllowance.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
console.log(props.data)
|
||||
const emit = defineEmits(["accept", "deny", "fix"])
|
||||
</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>
|
||||
Reference in New Issue
Block a user