Add collected order invoice management and improve edit logic

Introduce collected order invoice views, tables, and detailed navigation for superuser dashboards. Enhance `showEditOrderItemForm` to support an optional callback, streamlining post-submission actions. These changes improve invoice handling and editing workflows for better usability.
This commit is contained in:
Jepp9350
2025-03-03 15:36:40 +01:00
parent 30d8f5f2cb
commit 5567a43c9a
9 changed files with 329 additions and 4 deletions
@@ -236,7 +236,7 @@ loadOrder();
icon="fas fa-edit"
template="warning"
:disabled="!SessionUser.hasPermission('edit_order_items')"
@click="showEditOrderItemForm(orderItem.id, orderItem.product.name, orderItem.price, orderItem.notes, orderItem.reference, orderItem.quantity, editPossible).then(() => loadOrderItems())"
@click="showEditOrderItemForm(orderItem.id, orderItem.product.name, orderItem.price, orderItem.notes, orderItem.reference, orderItem.quantity, editPossible, () => loadOrderItems())"
/>
</template>
</ActionSettingsWheelButton>
@@ -329,7 +329,7 @@ loadOrder();
icon="fas fa-edit"
template="warning"
:disabled="!SessionUser.hasPermission('edit_order_items')"
@click="showEditOrderItemForm(relatedItem.id, relatedItem.product.name, relatedItem.price, relatedItem.notes, relatedItem.reference, relatedItem.quantity, editPossible).then(() => loadOrderItems())"
@click="showEditOrderItemForm(relatedItem.id, relatedItem.product.name, relatedItem.price, relatedItem.notes, relatedItem.reference, relatedItem.quantity, editPossible, () => loadOrderItems())"
/>
</template>
</ActionSettingsWheelButton>
@@ -0,0 +1,110 @@
<script setup>
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
import {getCustomerName} from "@/components/shop/POSDepartmentProcess.vue";
defineProps(['objects']);
import { ref } from 'vue';
import { loadList } from "@/components/pagination/paginatedList.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
const redirect = (path) => {
window.location = path;
}
</script>
<template>
<table class="table is-fullwidth is-hoverable is-striped">
<thead>
<tr>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.id.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.customer_number.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.name.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.notes.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.processor.label }}</th>
<th>Transaktioner</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.closed_at.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.created_at.label }}</th>
<th><!-- Actions --></th>
</tr>
</thead>
<tbody>
<tr v-for="object in objects" :key="object.id">
<td>{{ object.id }}</td>
<!-- Customer number -->
<EditableTableColumn
:object="object"
:loadList="loadList"
column="customer_id"
/>
<!-- Name -->
<EditableTableColumn
:object="object"
:loadList="loadList"
:editFunction="SessionUser.objects.collectedOrderInvoices.showEditObjectFieldForm"
column="name"
/>
<!-- Notes -->
<EditableTableColumn
:object="object"
:loadList="loadList"
:editFunction="SessionUser.objects.collectedOrderInvoices.showEditObjectFieldForm"
column="notes"
/>
<!-- Processor -->
<EditableTableColumn
:object="object"
:loadList="loadList"
:editFunction="SessionUser.objects.collectedOrderInvoices.showEditObjectFieldForm"
column="processor"
/>
<!-- Transactions -->
<EditableTableColumn
:object="object"
:loadList="loadList"
column="orders"
/>
<!-- Closed at -->
<EditableTableColumn
:object="object"
:loadList="loadList"
column="closed_at"
/>
<!-- Created at -->
<EditableTableColumn
:object="object"
:loadList="loadList"
column="created_at"
/>
<!-- Actions -->
<td>
<ActionSettingsWheelButton>
<template #actions>
<ActionSettingsWheelItem
icon="fas fa-eye"
@click="redirect('/superuser/invoices/' + object.id)"
label="Vis"
/>
</template>
</ActionSettingsWheelButton>
</td>
</tr>
<!-- If the list is empty -->
<tr v-if="objects.length === 0">
<td colspan="9">
Ingen transaktioner fundet
</td>
</tr>
<!-- Show a count of the total number of objects -->
<tr>
<td colspan="9">Viser {{ objects.length }} transaktioner i faktura kollektion</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
</style>
@@ -6,6 +6,8 @@ defineProps(['objects']);
import { ref } from 'vue';
import { loadList } from "@/components/pagination/paginatedList.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
const redirect = (path) => {
window.location = path;
@@ -76,6 +78,18 @@ const redirect = (path) => {
:loadList="loadList"
column="created_at"
/>
<!-- Actions -->
<td>
<ActionSettingsWheelButton>
<template #actions>
<ActionSettingsWheelItem
icon="fas fa-eye"
@click="redirect('/superuser/invoices/' + object.id)"
label="Vis"
/>
</template>
</ActionSettingsWheelButton>
</td>
</tr>
<!-- If the list is empty -->
<tr v-if="objects.length === 0">
@@ -5,7 +5,7 @@ import Swal from "sweetalert2";
//
export const showEditOrderItemForm = async (orderItemId, name, price, notes, reference, quantity, allowEdit) => {
export const showEditOrderItemForm = async (orderItemId, name, price, notes, reference, quantity, allowEdit, afterSubmit = null) => {
await Swal.fire({
title: 'Edit Item',
html: `
@@ -53,6 +53,10 @@ export const showEditOrderItemForm = async (orderItemId, name, price, notes, ref
.then(() => {
// Close the modal
Swal.close();
// If afterSubmit is set, run it
if (afterSubmit) {
afterSubmit();
}
})
.catch((e) => {
Swal.fire('Error', e.response.data.error ?? e.response, 'error');
@@ -144,7 +144,22 @@ export const CollectedOrderInvoices = {
return ObjectsGlobal.get.object(CollectedOrderInvoices.meta.endpoint, id);
}
},
functions: {},
functions: {
economic: {
invoice: async (invoiceId) => {
return authenticatedRequest('/collected-invoices/economic', 'POST', {
id: parseInt(invoiceId)
}).then((response) => {
console.log(response);
Swal.fire({
title: 'Fakturer med E-conomic',
text: 'Fakturaen er nu sendt til E-conomic',
icon: 'success'
});
});
}
}
},
/**
* Show the create object form
* @param onAfterSubmit
+8
View File
@@ -69,6 +69,8 @@ import SuperUserRolesPermissions from "@/views/dashboards/superUserDashboard/rol
import DepartmentStripeTerminalsReaders from "@/views/dashboards/superUserDashboard/department/stripe/DepartmentStripeTerminalsReaders.vue";
import DepartmentStripeSetup from "@/views/dashboards/superUserDashboard/department/stripe/DepartmentStripeSetup.vue";
import CollectedOrderInvoices from "@/views/dashboards/superUserDashboard/CollectedOrderInvoices.vue";
import CollectedOrderInvoice
from "@/views/dashboards/superUserDashboard/collectedOrderInvoice/collectedOrderInvoice.vue";
// Export the router as router
export const router = createRouter({
@@ -248,6 +250,12 @@ export const router = createRouter({
component: CollectedOrderInvoices,
meta: { middleware: superUserMiddleware }
},
{
name: 'collectedorderinvoice',
path: '/superuser/invoices/:collectedOrderInvoiceId',
component: CollectedOrderInvoice,
meta: { middleware: superUserMiddleware }
},
{
name: 'orders',
path: '/superuser/orders',
@@ -0,0 +1,68 @@
<script setup>
import SuperUserDashboardNavigation from "@/views/dashboards/superUserDashboard/SuperUserDashboardNavigation.vue";
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import PageTitle from "@/components/global/PageTitle.vue";
import { useRouter } from "vue-router";
import { ref } from 'vue';
import CollectedOrderInvoiceOrdersTable
from "@/components/displays/superuser/tables/collectedOrderInvoiceOrdersTable.vue";
import CollectedOrderInvoiceOverview
from "@/views/dashboards/superUserDashboard/collectedOrderInvoice/displays/collectedOrderInvoiceOverview.vue";
import CollectedOrderInvoiceManage
from "@/views/dashboards/superUserDashboard/collectedOrderInvoice/displays/collectedOrderInvoiceManage.vue";
// Get the id from the URL
const router = useRouter();
const collectedOrderInvoiceId = router.currentRoute.value.params.collectedOrderInvoiceId;
// Define the reactive objects
const orders = ref([]);
const data = ref([]);
// Make the first letter uppercase
const title = SessionUser.objects.collectedOrderInvoices.meta.labels.single;
const firstLetter = title.charAt(0).toUpperCase();
const restOfTitle = title.slice(1);
const formattedTitle = firstLetter + restOfTitle;
// Get the collected order invoice
const collectedOrderInvoice = ref(null);
const getCollectedOrderInvoice = async () => {
SessionUser.objects.collectedOrderInvoices.get.single(collectedOrderInvoiceId).then((response) => {
const raw_data = response;
console.log(raw_data);
orders.value = raw_data.orders || [];
collectedOrderInvoice.value = raw_data;
});
};
// Get the collected order invoice
getCollectedOrderInvoice();
</script>
<template>
<RestrictedPageWrapper :hasPermission="SessionUser.canAccessSuperUser()">
<SuperUserDashboardNavigation />
<PageTitle
:title="formattedTitle + ' #' + collectedOrderInvoiceId"
subtitle="Her kan du se en faktura i systemet"
/>
<!-- At A Glance -->
<collected-order-invoice-overview
v-bind:invoices="orders"
v-bind:closed_at="collectedOrderInvoice.closed_at"
:total="0"
/>
<CollectedOrderInvoiceManage
:invoiceId="parseInt(collectedOrderInvoiceId)"
v-bind:closed_at="collectedOrderInvoice.closed_at"
/>
<CollectedOrderInvoiceOrdersTable :objects="orders" />
</RestrictedPageWrapper>
</template>
<style scoped>
</style>
@@ -0,0 +1,32 @@
<script setup>
import {ref, defineProps} from 'vue';
import {SessionUser} from "@/components/session/token/SessionUser.vue";
const props = defineProps({
invoiceId: {
type: Number,
required: true,
},
closed_at: {
type: String,
required: false,
},
});
</script>
<template>
<button
v-if="!closed_at"
class="button is-small is-primary"
@click="SessionUser.objects.collectedOrderInvoices.functions.economic.invoice(invoiceId)"
>
<span class="icon">
<i class="fas fa-file-invoice-dollar"></i>
</span>
<span>Send til E-conomic (Som faktura klar til bogføring)</span>
</button>
</template>
<style scoped>
</style>
@@ -0,0 +1,74 @@
<script setup>
import {defineProps, ref} from 'vue';
const props = defineProps({
invoices: {
type: Array,
required: true,
description: 'The transactions contained in the invoice collection'
},
total: {
type: Number,
required: true,
description: 'The total amount of the invoices in the collection'
},
closed_at: {
type: String,
required: false,
description: 'The date the invoice collection was closed'
},
});
const column_width = 'is-3';
const displays = [
{
title: 'Transaktioner',
value: props.invoices.length,
icon: 'fas fa-file-invoice-dollar',
},
{
title: 'Total beløb',
value: (props.total ? props.total.toFixed(2) : 0) + ' kr.',
icon: 'fas fa-money-bill-wave',
},
{
title: 'Arkiveret',
value: props.closed_at ? props.closed_at : 'Nej',
icon: 'fas fa-lock',
},
];
</script>
<template>
<div class="columns is-multiline">
<template v-for="display in displays">
<div class="column" :class="column_width">
<div class="card">
<!-- Header -->
<header class="card-header">
<!-- Icon -->
<a class="card-header-icon">
<span class="icon">
<i :class="display.icon"></i>
</span>
</a>
<!-- Title -->
<p class="card-header-title">{{ display.title }}</p>
<!-- Dropdown icon -->
<a class="card-header-icon">
<span class="icon">
<i class="fas fa-angle-down"></i>
</span>
</a>
</header>
<div class="card-content">
<p class="title is-4">{{ display.value }}</p>
<p class="subtitle is-6">{{ display.title }}</p>
</div>
</div>
</div>
</template>
</div>
</template>
<style scoped>
</style>