Add CollectedOrderInvoicesListPagination and CollectedOrderInvoicesDefaultTable components for handling and displaying collected order invoices with advanced filtering and pagination options

This commit is contained in:
Jeppe Bundgaard
2026-02-03 12:24:13 +01:00
parent 95470794f5
commit 344acf4a13
4 changed files with 226 additions and 0 deletions
@@ -0,0 +1,99 @@
<script setup>
import CollectedOrderInvoicesDefaultTable from "@/components/displays/superuser/tables/CollectedOrderInvoicesDefaultTable.vue";
const props = defineProps({
setCustomerFilter: {
type: Number,
default: null
},
hideSearch: {
type: Boolean,
default: false
},
autoLoad: {
type: Boolean,
default: true
},
onlyReadyToInvoice: {
type: Boolean,
default: false
},
tableProps: {
type: Object,
default: () => ({})
},
displayClosed: {
type: Boolean,
default: true
},
displayBooked: {
type: Boolean,
default: true
},
});
import { defineProps } from "vue";
import { useRouter } from "vue-router";
import {
isLoaded,
isLoading,
list,
loadList,
metaCurrentPage,
metaItemsPerPage,
metaTotalItems,
setEndpoint,
setMetaItemsPerPage,
setPage,
metaSearch,
search,
setFilter,
setOrder,
hideSearchField,
setHideSearchField,
} from "@/components/pagination/paginatedList.vue";
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
import OrdersTable from "@/components/displays/department/pos/orders/ordersTable.vue";
import CategoriesTable from "@/components/displays/superuser/tables/categoriesTable.vue";
const router = useRouter();
if (!props.onlyReadyToInvoice || props.onlyReadyToInvoice === false) {
setEndpoint("/collected-invoices", false);
} else {
setEndpoint("/collected-invoices/ready-to-invoice", false);
}
setOrder("closed_at", "asc", false);
// Hide the search field if the prop is set to true
setHideSearchField(props.hideSearch);
if (props.setCustomerFilter) {
setFilter("customer_number", props.setCustomerFilter, false);
}
if (!props.displayClosed) {
setFilter("closed_at", 'is_null', false);
}
if (!props.displayBooked) {
setFilter("booked_invoice_id", 'is_null', false);
}
if (props.autoLoad) {
loadList();
}
</script>
<template>
<input @input="search($event.target.value)" class="input" type="text" :placeholder="'Søg efter ' + SessionUser.objects.collectedOrderInvoices.meta.labels.single" v-if="!hideSearchField"/>
<PaginationDisplay :metaItemsPerPage="metaItemsPerPage" :loadFunction="loadList" :isLoading="isLoading" :setMetaItemsPerPage="setMetaItemsPerPage"/>
<CollectedOrderInvoicesDefaultTable v-bind:objects="list" />
<PaginationNavigation :currentPage="metaCurrentPage" :totalPages="Math.ceil(metaTotalItems / metaItemsPerPage)" :loadFunction="loadList" :setPage="setPage" :isLoading="isLoading" />
<LoadButtonWhileAwait class="is-dark" :isLoading="isLoading" :loadFunction="loadList" icon="fas fa-sync-alt">Reload</LoadButtonWhileAwait>
</template>
<style scoped>
</style>
@@ -0,0 +1,89 @@
<script setup>
import { defineProps } from '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 props = defineProps({
objects: {
type: Array,
default: () => [],
},
});
const redirect = (path) => {
window.open(path, '_blank');
}
const parseProcessor = (value) => {
if (value === null) return SessionUser.objects.global.language.no_data;
if (value === 1) return 'E-conomic';
return SessionUser.objects.global.language.other;
}
const parseDate = (value) => {
if (value === null) return '-';
return value;
}
const parseClosedAt = (value) => {
if (value === null) return 'Åben';
return 'Lukket ' + value;
}
</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>{{ SessionUser.objects.collectedOrderInvoices.columns.external_id.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.po_number.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.closed_at.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.created_at.label }}</th>
<th>{{ SessionUser.objects.collectedOrderInvoices.columns.updated_at.label }}</th>
<th class="has-text-right">Handlinger</th>
</tr>
</thead>
<tbody>
<tr v-for="object in props.objects" :key="object.id">
<td>{{ object.id }}</td>
<td>{{ object.customer_number }}</td>
<td>{{ object.name ?? '-' }}</td>
<td>{{ object.notes ?? '-' }}</td>
<td>{{ parseProcessor(object.processor) }}</td>
<td>{{ object.external_id ?? '-' }}</td>
<td>{{ object.po_number ?? '-' }}</td>
<td>{{ parseClosedAt(object.closed_at) }}</td>
<td>{{ parseDate(object.created_at) }}</td>
<td>{{ parseDate(object.updated_at) }}</td>
<td>
<div class="buttons is-float-right">
<ActionSettingsWheelButton>
<template #actions>
<ActionSettingsWheelItem
icon="fas fa-eye"
@click="redirect('/superuser/invoices/' + object.id)"
label="Vis"
/>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
<tr v-if="objects.length === 0">
<td colspan="11">
Ingen {{ SessionUser.objects.collectedOrderInvoices.meta.labels.multiple }} fundet
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
</style>
@@ -345,6 +345,38 @@ export const CollectedOrderInvoices = {
throw error;
});
},
compareToEconomic: async (id) => {
/**
* Result example:
* {
* "success": true,
* "data": {
* "collected_invoice_id": 10133,
* "draft_id": null,
* "booked_id": 28368,
* "warnings": [
* "Error fetching draft ID: Invoice draft with external id (2ced7a3f-fab2-edbd-075f-ce981867c340) not found"
* ],
* "draft_total": null,
* "booked_total": 694,
* "difference": null,
* "internal_total": 694
* },
* "meta": [],
* "includes": []
* }
*/
// Send the request
return authenticatedRequest('/collected-invoices/economic/compare', 'GET', {
collected_invoice_id: parseInt(id)
}).then((response) => {
console.warn(response);
return response;
}).catch((error) => {
console.warn(error);
throw error;
});
},
// The additionalBody can contain:
// - send_as_is: boolean (default: false) - If true, the invoice will be sent to E-conomic as is, without any modifications made by fixed pricing or vehicle subscriptions.
invoice: async (invoiceId, additionalBody = {
@@ -4,6 +4,8 @@ import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrap
import {SessionUser} from "@/components/session/token/SessionUser.vue";
import CollectedOrderInvoicesPagination
from "@/components/displays/pagination/models/SuperUserDashboard/CollectedOrderInvoicesPagination.vue";
import CollectedOrderInvoicesListPagination
from "@/components/displays/pagination/models/SuperUserDashboard/CollectedOrderInvoicesListPagination.vue";
import PageTitle from "@/components/global/PageTitle.vue";
import ElementTabsBox from "@/components/displays/boxes/ElementTabsBox.vue";
import {onMounted, ref, watch} from "vue";
@@ -17,6 +19,7 @@ import InvoicingBillingPeriod
const tabs = ref([
{ name: 'Overblik', slot: 'overview', icon: 'fas fa-chart-pie', hidden: false },
{ name: 'Fakturaer', slot: 'invoices', icon: 'fas fa-list', hidden: false },
//{ name: SessionUser.objects.orders.meta.title, slot: 'transactions', icon: 'fas fa-shopping-cart', hidden: false },
{ name: 'Periode', slot: 'period', icon: 'fas fa-calendar-alt', hidden: false },
//{ name: 'Faktura pr. måned', slot: 'monthly', icon: 'fas fa-list', hidden: false },
@@ -70,6 +73,9 @@ onMounted(() => {
<template #overview>
<CollectedOrderInvoicesOverview />
</template>
<template #invoices>
<CollectedOrderInvoicesListPagination :auto-load="true" />
</template>
<!--<template #transactions>
<InvoiceOrdersPagination :auto-load="true" :invoice-view="true"/>
</template>-->