- Deleted `EdgeGatewayManager.vue` alongside its components, templates, styles, and functionality. - Added tests for `fetchDepartmentOrderBookingCount`. - Introduced `adminBookingCount.js` for department booking count logic.
1899 lines
77 KiB
Vue
1899 lines
77 KiB
Vue
<script setup>
|
|
import { useI18n } from "vue-i18n";
|
|
|
|
const { t } = useI18n();
|
|
|
|
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
|
|
import OrderAttachmentsActionButton from "@/components/displays/department/pos/orders/OrderAttachmentsActionButton.vue";
|
|
import AssignDraftOrderCustomerModal from "@/components/displays/modals/AssignDraftOrderCustomerModal.vue";
|
|
|
|
const props = defineProps({
|
|
orders: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
invoiceView: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
allowSelectMultiple: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
isCustomerView: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
groupInvoiceCollection: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
showOnlyWithIds: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
autoExpandAll: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
excludedOrderIds: {
|
|
type: Array, // Any order ids that should be excluded from the list, will also be excluded from calculations (e.g. hidden orders).
|
|
default: () => [],
|
|
},
|
|
showDraftAssignmentActions: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
import { computed, onMounted, ref, watch } from "vue";
|
|
import { useRoute } from "vue-router";
|
|
import { departments, getDepartments, isLoading, getDepartmentName } from "@/components/pagination/departmentTabs.vue";
|
|
import { showPopper, removePopperIfOpen, popperBox } from "@/components/displays/PopperDefault.vue";
|
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
import { orderBy, orderDirection, setOrder, loadList } from "@/components/pagination/paginatedList.vue";
|
|
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
|
|
import OrderContentTable from "@/components/displays/superuser/tables/OrderContentTable.vue";
|
|
import InvoiceMultipleCollectionsModal from "@/components/displays/modals/InvoiceMultipleCollectionsModal.vue";
|
|
import Swal from "sweetalert2";
|
|
import ColorIndicator from "@/components/displays/buttons/ColorIndicator.vue";
|
|
import InvoicingBillingPeriodInvoiceProgressBar from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/displays/InvoicingBillingPeriodInvoiceProgressBar.vue";
|
|
import { invoiceQueue } from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportInvoiceQueue.vue";
|
|
import PosDepartmentStepMobileAttachment from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobileAttachment.vue";
|
|
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
|
|
import ViewportResponsiveWrapper from "@/components/viewport/conditions/elements/ViewportResponsiveWrapper.vue";
|
|
import WhiteBoxCard from "@/components/displays/boxes/WhiteBoxCard.vue";
|
|
import { dispatchNavigationCountRefresh } from "@/components/models/navigation/items/navigationCountEvents.js";
|
|
import { isSystemUserId } from "@/components/session/token/SessionUser/Objects/systemUserIds.js";
|
|
|
|
const route = useRoute();
|
|
|
|
const redirectDepartmentOrderPage = (orderId, departmentId, newTab = true) => {
|
|
// Check if the user has access to the department
|
|
if (!SessionUser.canAccessDepartment(departmentId)) {
|
|
window.open("/user/orders/" + orderId, newTab ? "_blank" : "_self");
|
|
} else {
|
|
// Send the user to the order page (In a new tab)
|
|
// `/admin/${departmentId}/modules/pos/orders/${orderId}`
|
|
window.open(`/admin/${departmentId}/modules/pos/orders/${orderId}`, newTab ? "_blank" : "_self");
|
|
}
|
|
};
|
|
|
|
const redirectSuperUserInvoiceCollectionPage = (invoiceCollectionId) => {
|
|
// Send the user to the invoice collection page (In a new tab)
|
|
window.open(`/superuser/invoices/${invoiceCollectionId}`, "_blank");
|
|
};
|
|
|
|
// Get the departments (If the departments are not already loaded)
|
|
if (departments.value.length === 0) {
|
|
getDepartments();
|
|
}
|
|
|
|
const getEconomicInvoiceModule = (order) => order?.economic_invoice_module ?? null;
|
|
const getStripeInvoiceModule = (order) => order?.stripe_invoice_module ?? null;
|
|
const getInvoiceCollection = (order) => order?.invoice_collection ?? null;
|
|
|
|
const isOrderInvoicedWithEconomic = (order) => {
|
|
const economicInvoiceModule = getEconomicInvoiceModule(order);
|
|
if (!economicInvoiceModule) {
|
|
return false;
|
|
}
|
|
|
|
return economicInvoiceModule.invoice_id !== null || economicInvoiceModule.invoice_draft_id !== null;
|
|
};
|
|
|
|
const isOrderEconomicInvoiceBooked = (order) => {
|
|
const economicInvoiceModule = getEconomicInvoiceModule(order);
|
|
if (!economicInvoiceModule) {
|
|
return false;
|
|
}
|
|
|
|
return economicInvoiceModule.invoice_id !== null;
|
|
};
|
|
|
|
const isOrderInvoicedWithStripe = (order) => {
|
|
return getStripeInvoiceModule(order) !== null;
|
|
};
|
|
|
|
const isOrderInvoiced = (order) => {
|
|
return isOrderInvoicedWithEconomic(order) || isOrderInvoicedWithStripe(order);
|
|
};
|
|
|
|
const doesOrderHaveInvoiceCollection = (order) => {
|
|
return getInvoiceCollection(order) !== null;
|
|
};
|
|
|
|
const isOrderInvoiceCollectionClosed = (order) => {
|
|
const invoiceCollection = getInvoiceCollection(order);
|
|
if (!invoiceCollection) {
|
|
return false;
|
|
}
|
|
|
|
return invoiceCollection.closed_at !== null;
|
|
};
|
|
|
|
const isOrderInvoiceCollectionBooked = (order) => {
|
|
const invoiceCollection = getInvoiceCollection(order);
|
|
if (!invoiceCollection) {
|
|
return false;
|
|
}
|
|
|
|
return invoiceCollection.booked_invoice_id !== null;
|
|
};
|
|
|
|
const isOrderStripeInvoicePaid = (order) => {
|
|
const stripeInvoiceModule = getStripeInvoiceModule(order);
|
|
if (stripeInvoiceModule) {
|
|
return stripeInvoiceModule.paid;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const doesOrderHaveErrorMessage = (order) => {
|
|
return order.error_message !== null && order.error_message !== undefined;
|
|
};
|
|
|
|
const getOrderInvoiceStatusBarColor = (order) => {
|
|
// Check the invoice status
|
|
let color_class = "has-text-danger";
|
|
if (isOrderInvoicedWithStripe(order)) {
|
|
isOrderStripeInvoicePaid(order) ? (color_class = "has-text-success") : (color_class = "has-text-warning");
|
|
}
|
|
if (isOrderInvoicedWithEconomic(order)) {
|
|
isOrderEconomicInvoiceBooked(order) ? (color_class = "has-text-success") : (color_class = "has-text-warning");
|
|
}
|
|
if (doesOrderHaveInvoiceCollection(order)) {
|
|
if (isPendingHandheld(order) && !order?.completed_at) {
|
|
return "has-text-warning"; // If the order is pending handheld, show a warning color
|
|
}
|
|
color_class = "has-text-grey";
|
|
// Check if the invoice collection is closed
|
|
if (isOrderInvoiceCollectionClosed(order)) {
|
|
color_class = "has-text-warning";
|
|
// Check if the invoice collection is booked
|
|
if (isOrderInvoiceCollectionBooked(order)) {
|
|
color_class = "has-text-success";
|
|
}
|
|
}
|
|
}
|
|
if (doesOrderHaveErrorMessage(order)) {
|
|
color_class = "has-text-danger";
|
|
}
|
|
// Return the color class
|
|
return color_class;
|
|
};
|
|
|
|
const getOrderInvoiceStatusContent = (order) => {
|
|
const content = [];
|
|
const economicInvoiceModule = getEconomicInvoiceModule(order);
|
|
const stripeInvoiceModule = getStripeInvoiceModule(order);
|
|
const invoiceCollection = getInvoiceCollection(order);
|
|
if (isOrderInvoiced(order)) {
|
|
// If the order is invoiced with e-conomic, show the invoice id
|
|
if (isOrderInvoicedWithEconomic(order)) {
|
|
isOrderEconomicInvoiceBooked(order)
|
|
? content.push("Bogført i e-conomic")
|
|
: content.push("Gemt som kladde i e-conomic");
|
|
isOrderEconomicInvoiceBooked(order)
|
|
? content.push("Faktura ID: <strong>" + (economicInvoiceModule?.invoice_id ?? "-") + "</strong>")
|
|
: content.push("Faktura Kladde ID: <strong>" + (economicInvoiceModule?.invoice_draft_id ?? "-") + "</strong>");
|
|
}
|
|
// If the order is invoiced with stripe, show the invoice id
|
|
if (isOrderInvoicedWithStripe(order)) {
|
|
isOrderStripeInvoicePaid(order)
|
|
? content.push("Fakturaen er <strong>betalt</strong>")
|
|
: content.push("Fakturaen er <strong>ikke betalt</strong>");
|
|
content.push("Faktura ID: <strong>" + (stripeInvoiceModule?.id ?? "-") + "</strong>");
|
|
}
|
|
content.push(""); // Add a line break
|
|
isOrderInvoicedWithEconomic(order)
|
|
? content.push('<span class="has-text-grey">Faktureret med e-conomic</span>')
|
|
: content.push('<span class="has-text-grey">Faktureret med stripe</span>');
|
|
} else {
|
|
// Check if the order has an invoice collection
|
|
if (doesOrderHaveInvoiceCollection(order)) {
|
|
// Check if the invoice collection is closed
|
|
if (isOrderInvoiceCollectionClosed(order)) {
|
|
content.push("Faktura samlingen er <strong>lukket</strong>");
|
|
// Check if the invoice collection is booked
|
|
if (isOrderInvoiceCollectionBooked(order)) {
|
|
content.push("Faktura samlingen er <strong>bogført</strong>");
|
|
}
|
|
}
|
|
content.push(""); // Add a line break
|
|
content.push("Faktura samling ID: <strong>" + (invoiceCollection?.id ?? "-") + "</strong>");
|
|
// Add the Error message if it exists
|
|
if (doesOrderHaveErrorMessage(order)) {
|
|
content.push('Fejl: <strong class="has-text-danger">' + order.error_message + "</strong>");
|
|
}
|
|
} else {
|
|
content.push(""); // Add a line break
|
|
content.push('<strong class="has-text-grey">Transaktionen er ikke faktureret</strong>');
|
|
}
|
|
}
|
|
// Return the list of content as a string, with line breaks between each item
|
|
return content.join("<br>");
|
|
};
|
|
|
|
const getOrderInvoiceStatusTitle = (order) => {
|
|
return isOrderInvoiced(order) ? "Faktura status" : "Transaktion status";
|
|
};
|
|
|
|
const clickUser = (customer_id) => {
|
|
if (!SessionUser.canAccessSuperUser()) {
|
|
return;
|
|
}
|
|
// Send the user to the user page
|
|
SessionUser.adminUser.customers.fromCustomerNumber
|
|
.getUserId(customer_id)
|
|
.then((response) => window.open("/superuser/users/" + response.data.data.user_id, "_blank"));
|
|
};
|
|
|
|
const truncateOrderField = (value, length = 25) => {
|
|
return SessionUser.functions.text.truncate(value ?? "", length);
|
|
};
|
|
|
|
const normalizeInvoiceCollectionId = (invoiceCollectionId) => {
|
|
const parsedInvoiceCollectionId = Number.parseInt(invoiceCollectionId, 10);
|
|
return Number.isInteger(parsedInvoiceCollectionId) && parsedInvoiceCollectionId > 0
|
|
? parsedInvoiceCollectionId
|
|
: null;
|
|
};
|
|
|
|
const getInvoiceCollectionResponseOrders = (response) => {
|
|
if (Array.isArray(response?.orders)) {
|
|
return response.orders;
|
|
}
|
|
|
|
if (Array.isArray(response?.data?.orders)) {
|
|
return response.data.orders;
|
|
}
|
|
|
|
if (Array.isArray(response?.includes?.orders)) {
|
|
return response.includes.orders;
|
|
}
|
|
|
|
return [];
|
|
};
|
|
|
|
const isColumnCurrentlyBeingSortedBy = (column) => {
|
|
// Check if the column is currently being sorted by
|
|
return orderBy.value === column;
|
|
};
|
|
|
|
const isCurrentSortDirectionAscending = () => {
|
|
// Check if the current sort direction is ascending
|
|
return orderDirection.value === "asc";
|
|
};
|
|
|
|
const isCurrentSortDirectionDescending = () => {
|
|
// Check if the current sort direction is descending
|
|
return orderDirection.value === "desc";
|
|
};
|
|
|
|
const onTableHeaderClick = (column) => {
|
|
// Check if the column is already sorted
|
|
if (orderBy.value === column) {
|
|
// If the column is already sorted, toggle the direction
|
|
const direction = orderDirection.value === "asc" ? "desc" : "asc";
|
|
setOrder(column, direction);
|
|
} else {
|
|
// If the column is not sorted, set the order to the new column and default to ascending
|
|
const direction = "asc";
|
|
setOrder(column, direction);
|
|
}
|
|
// Load the list
|
|
loadList();
|
|
};
|
|
|
|
const tableHeaders = ref([
|
|
{
|
|
name: "id",
|
|
title: SessionUser.objects.orders.columns.id.label,
|
|
sortable: true,
|
|
},
|
|
...(props.isCustomerView
|
|
? []
|
|
: [
|
|
{
|
|
name: "customer_id",
|
|
title: SessionUser.objects.orders.columns.customer_id.label,
|
|
sortable: true,
|
|
},
|
|
]),
|
|
...(props.isCustomerView
|
|
? []
|
|
: [
|
|
{
|
|
name: "cashier_id",
|
|
title: SessionUser.objects.orders.columns.cashier_id.label,
|
|
sortable: true,
|
|
},
|
|
]),
|
|
{
|
|
name: "department_id",
|
|
title: SessionUser.objects.orders.columns.department_id.label,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "reg_1",
|
|
title: SessionUser.functions.ucFirst(SessionUser.objects.vehicles.meta.labels.multiple),
|
|
sortable: true,
|
|
},
|
|
/**
|
|
* {
|
|
* name: 'reg_1',
|
|
* title: 'Reg. 1',
|
|
* sortable: true,
|
|
* },
|
|
* {
|
|
* name: 'reg_2',
|
|
* title: 'Reg. 2',
|
|
* sortable: true,
|
|
* },
|
|
* {
|
|
* name: 'reg_3',
|
|
* title: 'Reg. 3',
|
|
* sortable: true,
|
|
* },
|
|
*/
|
|
{
|
|
name: "reference",
|
|
title: SessionUser.objects.orders.columns.reference.label,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "notes",
|
|
title: SessionUser.objects.orders.columns.notes.label,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "po",
|
|
title: SessionUser.objects.orders.columns.po.label,
|
|
visible: (order) => {
|
|
return canSeePoField() || SessionUser.canAccessAdmin();
|
|
},
|
|
},
|
|
{
|
|
name: "created_at",
|
|
title: SessionUser.objects.orders.columns.created_at.label,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: "total_net_amount",
|
|
title: t("tables.orders.amount"),
|
|
sortable: false,
|
|
},
|
|
/**
|
|
* {
|
|
* name: 'completed_at',
|
|
* title: 'Gennemført',
|
|
* sortable: true,
|
|
* },
|
|
*/
|
|
{
|
|
name: "actions",
|
|
title: "",
|
|
sortable: false,
|
|
},
|
|
]);
|
|
|
|
const active_dropdown_object_ids = ref([]);
|
|
const toggleDropdown = (object) => {
|
|
// Check if the object is already in the active dropdown list
|
|
if (active_dropdown_object_ids.value.includes(object.id)) {
|
|
// If it is, remove it from the list
|
|
active_dropdown_object_ids.value = active_dropdown_object_ids.value.filter((id) => id !== object.id);
|
|
} else {
|
|
// If it is not, add it to the list
|
|
active_dropdown_object_ids.value.push(object.id);
|
|
}
|
|
};
|
|
const isDropdownActive = (object) => {
|
|
// If the expanded all prop is true, the active dropdown list should be exclusive instead of inclusive
|
|
// Check if the object is in the active dropdown list
|
|
const isObjectInList = active_dropdown_object_ids.value.includes(object.id);
|
|
return isAutoExpandAll() ? !isObjectInList : isObjectInList;
|
|
};
|
|
|
|
/**
|
|
* Invoice collection selection
|
|
*/
|
|
const selectedInvoiceCollections = ref([]);
|
|
const toggleInvoiceCollectionSelection = (invoiceCollectionId) => {
|
|
// Check if the invoice collection is already selected
|
|
if (selectedInvoiceCollections.value.includes(invoiceCollectionId)) {
|
|
// If it is, remove it from the list
|
|
selectedInvoiceCollections.value = selectedInvoiceCollections.value.filter((id) => id !== invoiceCollectionId);
|
|
} else {
|
|
// If it is not, add it to the list
|
|
selectedInvoiceCollections.value.push(invoiceCollectionId);
|
|
}
|
|
};
|
|
|
|
const isInvoiceCollectionSelected = (invoiceCollectionId) => {
|
|
// Check if the invoice collection is in the selected list
|
|
return selectedInvoiceCollections.value.includes(invoiceCollectionId);
|
|
};
|
|
const isInvoiceCollectionSelectedAll = () => {
|
|
// Check if all invoice collections are selected
|
|
return selectedInvoiceCollections.value.length === getUniqueInvoiceCollections().length;
|
|
};
|
|
const selectAllInvoiceCollections = () => {
|
|
// Check if all invoice collections are selected
|
|
if (isInvoiceCollectionSelectedAll()) {
|
|
// If they are, deselect all
|
|
selectedInvoiceCollections.value = [];
|
|
} else {
|
|
// If they are not, select all
|
|
selectedInvoiceCollections.value = getUniqueInvoiceCollections();
|
|
}
|
|
};
|
|
|
|
const getUniqueInvoiceCollections = () => {
|
|
// Check if there are no orders
|
|
if (props.orders ? props.orders.length === 0 : true) {
|
|
// If there are no orders, return an empty array
|
|
return [];
|
|
}
|
|
// Get a list of unique invoice collections from the orders
|
|
return [
|
|
...new Set(
|
|
props.orders
|
|
.map((order) => normalizeInvoiceCollectionId(order?.invoice_collection_id))
|
|
.filter((invoiceCollectionId) => invoiceCollectionId !== null)
|
|
),
|
|
];
|
|
};
|
|
|
|
const invoiceCollectionQueueInProgress = invoiceQueue.invoiceCollectionQueueInProgress;
|
|
const invoiceCollectionQueueFailed = invoiceQueue.invoiceCollectionQueueFailed;
|
|
const invoiceCollectionQueueSuccess = invoiceQueue.invoiceCollectionQueueSuccess;
|
|
const invoiceCollectionQueue = invoiceQueue.invoiceCollectionQueue;
|
|
const collectionQueueLog = invoiceQueue.collectionQueueLog;
|
|
const isInvoiceMultipleCollectionsModalOpen = ref(false);
|
|
const isInvoiceQueueBusy = computed(() => {
|
|
return invoiceCollectionQueue.value.length > 0 || invoiceCollectionQueueInProgress.value.length > 0;
|
|
});
|
|
|
|
const invoiceSelectedCollections = () => {
|
|
// Check if any invoice collections are selected
|
|
if (selectedInvoiceCollections.value.length === 0) {
|
|
return;
|
|
}
|
|
isInvoiceMultipleCollectionsModalOpen.value = true;
|
|
invoiceQueue.addInvoiceCollectionsToQueue(selectedInvoiceCollections.value);
|
|
invoiceQueue.processInvoiceCollectionQueue();
|
|
};
|
|
|
|
const retryInvoiceCollection = (invoiceCollectionId) => {
|
|
invoiceQueue.retryInvoiceCollection(parseInt(invoiceCollectionId));
|
|
};
|
|
|
|
const confirmCloseModal = () => {
|
|
Swal.fire({
|
|
title: "Er du sikker på at du vil lukke vinduet?",
|
|
text: "Du vil ikke kunne få den visualiseret igen!",
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#3085d6",
|
|
cancelButtonColor: "#d33",
|
|
confirmButtonText: "Ja, luk vinduet!",
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
isInvoiceMultipleCollectionsModalOpen.value = false;
|
|
}
|
|
});
|
|
};
|
|
|
|
const ordersSortedByInvoiceCollection = computed(() => {
|
|
// Sort the orders by invoice collection id
|
|
return [...props.orders].sort((a, b) => {
|
|
const leftInvoiceCollectionId = normalizeInvoiceCollectionId(a?.invoice_collection_id) ?? Number.MAX_SAFE_INTEGER;
|
|
const rightInvoiceCollectionId = normalizeInvoiceCollectionId(b?.invoice_collection_id) ?? Number.MAX_SAFE_INTEGER;
|
|
return leftInvoiceCollectionId - rightInvoiceCollectionId;
|
|
});
|
|
});
|
|
|
|
const redirectDepartmentBookingPage = (bookingId, departmentId, newTab = true) => {
|
|
// Send the user to the booking page (In a new tab)
|
|
window.open(`/admin/${departmentId}/modules/pos/bookings/${bookingId}`, newTab ? "_blank" : "_self");
|
|
};
|
|
|
|
const getVisibleOrdersSortedByInvoiceCollection = computed(() => {
|
|
// Get the orders that are visible and sorted by invoice collection id
|
|
return ordersSortedByInvoiceCollection.value.filter((order) => isObjectVisible(order));
|
|
});
|
|
|
|
const getVisibleOrders = computed(() => {
|
|
// Get the orders that are visible
|
|
return props.orders.filter((order) => isObjectVisible(order));
|
|
});
|
|
|
|
const getOrdersByInvoiceCollection = (invoiceCollectionId) => {
|
|
const normalizedInvoiceCollectionId = normalizeInvoiceCollectionId(invoiceCollectionId);
|
|
if (normalizedInvoiceCollectionId === null) {
|
|
return [];
|
|
}
|
|
// If the showOnlyWithIds prop is set, filter the orders by the ids in the array
|
|
if (props.showOnlyWithIds.length > 0) {
|
|
return props.orders.filter(
|
|
(order) =>
|
|
normalizeInvoiceCollectionId(order?.invoice_collection_id) === normalizedInvoiceCollectionId &&
|
|
props.showOnlyWithIds.includes(order.id)
|
|
);
|
|
}
|
|
// Get the orders by invoice collection id
|
|
return props.orders.filter(
|
|
(order) => normalizeInvoiceCollectionId(order?.invoice_collection_id) === normalizedInvoiceCollectionId
|
|
);
|
|
};
|
|
|
|
const isFirstOrderInCollection = (invoiceCollectionId, order) => {
|
|
// Check if the order is the first in the collection
|
|
const ordersInCollection = getOrdersByInvoiceCollection(invoiceCollectionId);
|
|
// Get the first order in the collection
|
|
const firstOrderInCollection = ordersInCollection[0];
|
|
// Check if the current order is the first in the collection
|
|
return firstOrderInCollection?.id === order.id;
|
|
};
|
|
const invoiceCollectionDetails = ref({}); // Used to provide the invoice collection booked id in the list
|
|
const invoiceCollectionOrderCounts = ref({});
|
|
const shouldLoadInvoiceCollectionOrderCounts = computed(() => {
|
|
return (
|
|
props.groupInvoiceCollection &&
|
|
SessionUser.canAccessSuperUser() &&
|
|
typeof SessionUser.objects?.collectedOrderInvoices?.get?.single === "function"
|
|
);
|
|
});
|
|
const resetInvoiceCollectionLookups = () => {
|
|
invoiceCollectionDetails.value = {};
|
|
invoiceCollectionOrderCounts.value = {};
|
|
};
|
|
const loadInvoiceCollectionOrderCounts = async () => {
|
|
resetInvoiceCollectionLookups();
|
|
if (!shouldLoadInvoiceCollectionOrderCounts.value) {
|
|
return;
|
|
}
|
|
// Get all unique invoice collection ids
|
|
const uniqueInvoiceCollections = getUniqueInvoiceCollections();
|
|
if (uniqueInvoiceCollections.length === 0) {
|
|
return;
|
|
}
|
|
// Loop through each unique invoice collection id
|
|
await Promise.all(
|
|
uniqueInvoiceCollections.map(async (invoiceCollectionId) => {
|
|
try {
|
|
// Get the orders for the invoice collection
|
|
const response = await SessionUser.objects.collectedOrderInvoices.get.single(invoiceCollectionId);
|
|
// Get the orders from the response
|
|
const orders = getInvoiceCollectionResponseOrders(response).filter(
|
|
(order) => props.excludedOrderIds.includes(order.id) === false
|
|
);
|
|
// Set the count for the invoice collection id
|
|
invoiceCollectionOrderCounts.value[invoiceCollectionId] = orders.length;
|
|
invoiceCollectionDetails.value[invoiceCollectionId] = response ?? null;
|
|
} catch (error) {
|
|
console.error("Error loading invoice collection order counts for: " + invoiceCollectionId, error);
|
|
// Set the count for the invoice collection id to 0 if there was an error
|
|
invoiceCollectionOrderCounts.value[invoiceCollectionId] = 0;
|
|
invoiceCollectionDetails.value[invoiceCollectionId] = null;
|
|
}
|
|
})
|
|
);
|
|
};
|
|
const getInvoiceCollectionOrderCount = (invoiceCollectionId) => {
|
|
const normalizedInvoiceCollectionId = normalizeInvoiceCollectionId(invoiceCollectionId);
|
|
if (normalizedInvoiceCollectionId === null) {
|
|
return 0;
|
|
}
|
|
// Get the count for the invoice collection id
|
|
return invoiceCollectionOrderCounts.value[normalizedInvoiceCollectionId] || 0;
|
|
};
|
|
const getInvoiceCollectionDetails = (invoiceCollectionId) => {
|
|
const normalizedInvoiceCollectionId = normalizeInvoiceCollectionId(invoiceCollectionId);
|
|
if (normalizedInvoiceCollectionId !== null && invoiceCollectionDetails.value[normalizedInvoiceCollectionId]) {
|
|
return invoiceCollectionDetails.value[normalizedInvoiceCollectionId];
|
|
}
|
|
return null;
|
|
};
|
|
watch(
|
|
[() => props.orders, () => props.excludedOrderIds, () => props.groupInvoiceCollection],
|
|
() => {
|
|
// Load the invoice collection order counts when the orders change
|
|
loadInvoiceCollectionOrderCounts();
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
const isObjectVisible = (object) => {
|
|
if (!SessionUser.canAccessSuperUser() && isSystemOrder(object)) {
|
|
return false; // Hide system orders for non-superusers
|
|
}
|
|
// Check if the object is visible based on the showOnlyWithIds prop
|
|
if (props.showOnlyWithIds.length === 0 && !props.excludedOrderIds.includes(object.id)) {
|
|
return true; // If no filter is applied, show all objects
|
|
} else if (props.excludedOrderIds.includes(object.id)) {
|
|
return false; // If the object is in the excluded list, hide it
|
|
}
|
|
// Check if the object id is in the showOnlyWithIds array
|
|
return props.showOnlyWithIds.includes(object.id);
|
|
};
|
|
const autoExpandAllToggled = ref(false); // When true, the prop.autoExpandAll value will be reversed. (This is used to toggle the auto expand all button in the UI)
|
|
|
|
const isAutoExpandAll = () => {
|
|
// Return the opposite of the autoExpandAll prop if the autoExpandAllToggled is true
|
|
return (props.autoExpandAll && !autoExpandAllToggled.value) || (!props.autoExpandAll && autoExpandAllToggled.value);
|
|
};
|
|
|
|
const toggleAutoExpandAll = () => {
|
|
// Toggle the auto expand all state
|
|
autoExpandAllToggled.value = !autoExpandAllToggled.value;
|
|
// Clear the active dropdown list
|
|
// This is to avoid confusion when toggling the auto expand all button
|
|
active_dropdown_object_ids.value = [];
|
|
};
|
|
|
|
const isSystemOrder = (order) => {
|
|
return isSystemUserId(order?.cashier_id);
|
|
};
|
|
|
|
const isPendingHandheld = (order) => {
|
|
return order.pending_handheld === true && order.completed_at === null;
|
|
};
|
|
|
|
const canSeePoField = () => {
|
|
return true;
|
|
//return (SessionUser.hasAttribute('usePONumbers') || SessionUser.hasPermission('department'))
|
|
};
|
|
|
|
/**
|
|
* Order actions modal
|
|
*/
|
|
const selectedOrderForActionsMenu = ref(null);
|
|
const isOrderActionsModalOpen = computed(() => {
|
|
return selectedOrderForActionsMenu.value !== null;
|
|
});
|
|
const closeOrderActionsModal = () => {
|
|
selectedOrderForActionsMenu.value = null;
|
|
};
|
|
const openOrderActionsModal = (order) => {
|
|
selectedOrderForActionsMenu.value = order;
|
|
};
|
|
const selectedOrderForDraftAssignment = ref(null);
|
|
const isDraftAssignmentModalOpen = computed(() => {
|
|
return selectedOrderForDraftAssignment.value !== null;
|
|
});
|
|
|
|
const canEditTransaction = (order) => {
|
|
return SessionUser.canAccessAdmin() || SessionUser.canAccessDepartment(order.department_id);
|
|
};
|
|
|
|
const currentRoutePath = computed(() => {
|
|
const resolvedRoutePath =
|
|
route && typeof route === "object" ? route.path || route.fullPath || route.name || "" : "";
|
|
|
|
if (resolvedRoutePath) {
|
|
return String(resolvedRoutePath);
|
|
}
|
|
|
|
if (typeof window !== "undefined") {
|
|
return String(window.location.pathname || "");
|
|
}
|
|
|
|
return "";
|
|
});
|
|
|
|
const shouldShowDraftAssignmentActions = computed(() => {
|
|
return Boolean(props.showDraftAssignmentActions) || currentRoutePath.value.includes("/modules/pos/drafts");
|
|
});
|
|
|
|
const canShowDraftAssignmentAction = () => {
|
|
return shouldShowDraftAssignmentActions.value;
|
|
};
|
|
|
|
const closeDraftAssignmentModal = () => {
|
|
selectedOrderForDraftAssignment.value = null;
|
|
};
|
|
|
|
const openDraftAssignmentModal = (order) => {
|
|
if (!canShowDraftAssignmentAction()) {
|
|
return;
|
|
}
|
|
|
|
closeOrderActionsModal();
|
|
selectedOrderForDraftAssignment.value = order;
|
|
};
|
|
|
|
const handleDraftAssignmentSuccess = async () => {
|
|
closeDraftAssignmentModal();
|
|
await loadList();
|
|
dispatchNavigationCountRefresh();
|
|
await Swal.fire({
|
|
icon: "success",
|
|
title: t("admin.pos.drafts_assignment.success"),
|
|
timer: 1800,
|
|
showConfirmButton: false,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Cashier name formatting
|
|
* This:
|
|
* - Writes "afdeling" if the cashiers name is the same as the department name.
|
|
* - Returns the first name, and first letter of the lastname of the cashier.
|
|
*/
|
|
const formatCashierName = (order) => {
|
|
if (getDepartmentName(order.department_id) === order.cashier_name) {
|
|
return SessionUser.functions.ucFirst(SessionUser.objects.departments.meta.labels.single);
|
|
}
|
|
return order.cashier_name;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<ViewportResponsiveWrapper>
|
|
<template #desktop>
|
|
<table class="table is-fullwidth">
|
|
<thead>
|
|
<tr v-if="props.invoiceView && props.allowSelectMultiple">
|
|
<td colspan="100%">
|
|
<div class="buttons">
|
|
<!-- Invoice selected collections -->
|
|
<button
|
|
class="button is-small"
|
|
@click="invoiceSelectedCollections()"
|
|
:disabled="selectedInvoiceCollections.length === 0 || isInvoiceQueueBusy"
|
|
>
|
|
{{ $t("global.invoice") }} {{ $t("global.selected_multiple") }} ({{
|
|
selectedInvoiceCollections.length
|
|
}})
|
|
</button>
|
|
<!-- Select / Unselect all invoice collections -->
|
|
<button
|
|
class="button is-small"
|
|
@click="selectAllInvoiceCollections()"
|
|
:class="{
|
|
'is-light': !isInvoiceCollectionSelectedAll(),
|
|
'is-dark': isInvoiceCollectionSelectedAll(),
|
|
}"
|
|
>
|
|
{{ isInvoiceCollectionSelectedAll() ? $t("global.unselect") : $t("global.select") }}
|
|
{{ $t("global.all").toLowerCase() }}
|
|
</button>
|
|
<!-- Expand / Collapse all invoice collections -->
|
|
<button
|
|
class="button is-small"
|
|
@click="toggleAutoExpandAll()"
|
|
:class="{
|
|
'is-light': !isAutoExpandAll(),
|
|
'is-dark': isAutoExpandAll(),
|
|
}"
|
|
>
|
|
{{ isAutoExpandAll() ? $t("global.collapse") : $t("global.expand") }}
|
|
{{ $t("global.all").toLowerCase() }}
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th class="is-narrow" v-if="props.invoiceView && props.allowSelectMultiple"></th>
|
|
<th class="is-narrow" v-if="props.invoiceView"></th>
|
|
<template v-for="tableHeaders in tableHeaders" :key="tableHeaders">
|
|
<template v-if="tableHeaders.visible === undefined || tableHeaders.visible(null) === true">
|
|
<th
|
|
v-if="tableHeaders.sortable"
|
|
:class="{
|
|
'is-clickable': tableHeaders.sortable,
|
|
'has-text-centered': tableHeaders.name === 'id',
|
|
}"
|
|
@click="onTableHeaderClick(tableHeaders.name)"
|
|
>
|
|
<span class="is-flex-wrap-nowrap">
|
|
<span class="has-text-weight-bold">{{ tableHeaders.title }}</span>
|
|
<span
|
|
v-if="isColumnCurrentlyBeingSortedBy(tableHeaders.name)"
|
|
:class="{
|
|
'fas fa-sort-up': isCurrentSortDirectionAscending(),
|
|
'fas fa-sort-down': isCurrentSortDirectionDescending(),
|
|
}"
|
|
class="icon is-small"
|
|
/>
|
|
</span>
|
|
</th>
|
|
<th
|
|
v-else
|
|
:class="{
|
|
'has-text-centered': tableHeaders.name === 'id',
|
|
}"
|
|
>
|
|
<span class="is-flex-wrap-nowrap">
|
|
<span class="has-text-weight-bold">{{ tableHeaders.title }}</span>
|
|
</span>
|
|
</th>
|
|
</template>
|
|
</template>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template
|
|
v-for="order in props.groupInvoiceCollection ? getVisibleOrdersSortedByInvoiceCollection : getVisibleOrders"
|
|
:key="order.id"
|
|
>
|
|
<!-- If grouping by invoice collection is enabled, show the invoice collection id -->
|
|
<template
|
|
v-if="
|
|
props.groupInvoiceCollection &&
|
|
doesOrderHaveInvoiceCollection(order) &&
|
|
isFirstOrderInCollection(order.invoice_collection_id, order)
|
|
"
|
|
>
|
|
<tr>
|
|
<td colspan="13">
|
|
<div class="has-text-weight-bold">
|
|
Faktura samling ID: {{ order.invoice_collection_id }}
|
|
<span class="has-text-grey"
|
|
>( {{ SessionUser.objects.global.language.showing }}
|
|
{{ getOrdersByInvoiceCollection(order.invoice_collection_id).length }}
|
|
{{ SessionUser.objects.global.language.showing_of_separator }}
|
|
{{ getInvoiceCollectionOrderCount(order.invoice_collection_id) }}
|
|
{{ SessionUser.objects.orders.meta.labels.multiple.toLowerCase() }} )</span
|
|
>
|
|
</div>
|
|
</td>
|
|
<!-- Spacer -->
|
|
<td colspan="2">
|
|
<!-- Total net amount for the invoice collection -->
|
|
<span class="has-text-weight-bold">
|
|
{{
|
|
SessionUser.functions.currency.toLocal(
|
|
getOrdersByInvoiceCollection(order.invoice_collection_id)
|
|
.reduce((sum, order) => sum + order.total_net_amount, 0)
|
|
.toFixed(2)
|
|
)
|
|
}}
|
|
</span>
|
|
</td>
|
|
<!-- Actions for the invoice collection -->
|
|
<td class="is-narrow">
|
|
<ActionSettingsWheelButton v-bind:invoice_collection_id="order.invoice_collection_id">
|
|
<template #actions> </template>
|
|
</ActionSettingsWheelButton>
|
|
</td>
|
|
</tr>
|
|
<!-- Warning, if there's orders in the collection that are filtered out -->
|
|
<tr
|
|
v-if="
|
|
getInvoiceCollectionOrderCount(order.invoice_collection_id) >
|
|
getOrdersByInvoiceCollection(order.invoice_collection_id).length
|
|
"
|
|
>
|
|
<td colspan="100%">
|
|
<div class="message is-warning">
|
|
<div class="message-body">
|
|
<span class="has-text-weight-bold">Bemærk:</span> Der er ordrer i denne faktura samling, som ikke
|
|
er synlige pga. filtre.
|
|
<br />
|
|
<span class="has-text-weight-bold"
|
|
>Disse
|
|
{{
|
|
getInvoiceCollectionOrderCount(order.invoice_collection_id) -
|
|
getOrdersByInvoiceCollection(order.invoice_collection_id).length
|
|
}}
|
|
er ikke vist, men vil muligvis blive faktureret alligevel.</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
<tr v-if="isObjectVisible(order)" :class="{ 'has-background-info-light': isSystemOrder(order) }">
|
|
<template v-if="props.invoiceView && props.allowSelectMultiple">
|
|
<td class="is-narrow">
|
|
<button
|
|
class="button is-small"
|
|
@click="toggleInvoiceCollectionSelection(order.invoice_collection_id)"
|
|
:class="{
|
|
'is-light': !isInvoiceCollectionSelected(parseInt(order.invoice_collection_id)),
|
|
'is-dark': isInvoiceCollectionSelected(parseInt(order.invoice_collection_id)),
|
|
}"
|
|
>
|
|
{{
|
|
isInvoiceCollectionSelected(parseInt(order.invoice_collection_id))
|
|
? SessionUser.objects.global.language.unselect
|
|
: SessionUser.objects.global.language.select
|
|
}}
|
|
</button>
|
|
</td>
|
|
</template>
|
|
<template v-if="props.invoiceView">
|
|
<td class="is-narrow">
|
|
<button class="button is-small" @click="toggleDropdown(order)">
|
|
<span
|
|
class="icon"
|
|
:class="isDropdownActive(order) ? 'has-text-link' : ''"
|
|
:style="isDropdownActive(order) ? 'transform: rotate(90deg);' : ''"
|
|
>
|
|
<!-- Dropdown icon -->
|
|
<i class="fas fa-angle-right"></i>
|
|
</span>
|
|
</button>
|
|
</td>
|
|
</template>
|
|
<td>
|
|
<ColorIndicator
|
|
v-bind:color_class="getOrderInvoiceStatusBarColor(order)"
|
|
v-bind:icon_class="isPendingHandheld(order) ? 'fas fa-mobile-alt' : 'fas fa-circle'"
|
|
v-bind:is_narrow="true"
|
|
v-bind:label="{
|
|
text: order.id,
|
|
classes: [],
|
|
max_length: 7,
|
|
}"
|
|
v-bind:visibility="{
|
|
icon: true,
|
|
dropdown: true,
|
|
}"
|
|
v-bind:dropdown_content="{
|
|
title: null,
|
|
buttons_title: null,
|
|
content: [
|
|
// Transaction
|
|
{
|
|
text: SessionUser.functions.ucFirst(SessionUser.objects.orders.meta.labels.single),
|
|
action: () => {
|
|
redirectDepartmentOrderPage(order.id, order.department_id);
|
|
},
|
|
...(SessionUser.canAccessSuperUser() ? {} : { disabled: true }),
|
|
button_classes: ['no-underline-text', 'is-text'],
|
|
button: true,
|
|
button_text: order.id,
|
|
v_centered: true,
|
|
},
|
|
// Invoice collection
|
|
{
|
|
text:
|
|
SessionUser.objects.global.language.invoice +
|
|
' ' +
|
|
SessionUser.objects.global.language.collection.toLowerCase(),
|
|
action: () => {
|
|
redirectSuperUserInvoiceCollectionPage(order.invoice_collection_id);
|
|
},
|
|
...(SessionUser.canAccessSuperUser() ? {} : { disabled: true }),
|
|
button_classes: ['no-underline-text', 'is-text'],
|
|
button: true,
|
|
button_text: order.invoice_collection_id,
|
|
v_centered: true,
|
|
},
|
|
/**
|
|
* // Completed status
|
|
* {
|
|
* text: SessionUser.objects.orders.columns.completed_at.label,
|
|
* action: () => {
|
|
* redirectDepartmentOrderPage(order.id, order.department_id);
|
|
* },
|
|
* ...(SessionUser.canAccessSuperUser() ? {} : { disabled: true }),
|
|
* button_classes: ['no-underline-text', 'is-text', ...(order.completed_at !== null ? [] : ['has-text-warning'])],
|
|
* button: true,
|
|
* button_text: (order.completed_at !== null ? SessionUser.objects.global.language.yes : SessionUser.objects.global.language.no),
|
|
* v_centered: true,
|
|
* },
|
|
*/
|
|
|
|
// Handheld status
|
|
...(isPendingHandheld(order)
|
|
? [
|
|
{
|
|
text: SessionUser.objects.global.language.handheld_pending_order,
|
|
action: () => {},
|
|
button_classes: ['no-underline-text', 'is-text', 'has-text-warning'],
|
|
button: true,
|
|
button_text: SessionUser.objects.global.language.confirmation_needed,
|
|
v_centered: true,
|
|
},
|
|
]
|
|
: []),
|
|
],
|
|
buttons: [],
|
|
}"
|
|
/>
|
|
</td>
|
|
<td v-if="!props.isCustomerView">
|
|
<ColorIndicator
|
|
v-bind:label="{
|
|
text: order.customer_name,
|
|
classes: [],
|
|
max_length: 7,
|
|
}"
|
|
v-bind:visibility="{
|
|
icon: false,
|
|
dropdown: true,
|
|
}"
|
|
v-bind:dropdown_content="{
|
|
title: null,
|
|
buttons_title: null,
|
|
content: [
|
|
{
|
|
text: order.customer_name,
|
|
action: () => {
|
|
clickUser(order.customer_id);
|
|
},
|
|
...(SessionUser.canAccessSuperUser() ? {} : { disabled: true }),
|
|
button: true,
|
|
button_text: 'Vis kunde',
|
|
},
|
|
],
|
|
buttons: [],
|
|
}"
|
|
/>
|
|
</td>
|
|
<!--
|
|
<td>
|
|
<span
|
|
@mouseover="showPopper(
|
|
popperBox(
|
|
'Customer ID',
|
|
order.customer_id,
|
|
),
|
|
$event.target
|
|
)"
|
|
@mouseleave="removePopperIfOpen()"
|
|
@click="clickUser(order.customer_id)"
|
|
:class="{ 'is-clickable': SessionUser.canAccessSuperUser() }"
|
|
>
|
|
{{ order.customer_name }}
|
|
</span>
|
|
</td> -->
|
|
<td v-if="!props.isCustomerView">
|
|
<template v-if="!isSystemOrder(order)">{{ formatCashierName(order) }}</template>
|
|
<template v-else><span class="has-text-grey-light">System</span></template>
|
|
</td>
|
|
<td>{{ getDepartmentName(order.department_id) }}</td>
|
|
<!-- Vehicles -->
|
|
<td>
|
|
<p>
|
|
<EditableTableColumn
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_1"
|
|
:parse-function="(value) => value || t('global.no_data')"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permissionCheckFunction="(orderobj) => canEditTransaction(order)"
|
|
/>
|
|
</p>
|
|
<p>
|
|
<EditableTableColumn
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_2"
|
|
:parse-function="(value) => value || '-'"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permissionCheckFunction="(bookingobj) => canEditTransaction(order)"
|
|
/>
|
|
</p>
|
|
<p v-show="order.reg_3">
|
|
<EditableTableColumn
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_3"
|
|
:parse-function="(value) => value || '-'"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permissionCheckFunction="(bookingobj) => canEditTransaction(order)"
|
|
/>
|
|
</p>
|
|
</td>
|
|
<EditableTableColumn
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reference"
|
|
:hover-text="order.reference"
|
|
:parse-function="(value) => truncateOrderField(value, 10)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => true"
|
|
/>
|
|
<EditableTableColumn
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="notes"
|
|
:parse-function="(value) => truncateOrderField(value, 10)"
|
|
:hover-text="order.notes"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => SessionUser.canAccessAdmin()"
|
|
/>
|
|
<EditableTableColumn
|
|
v-if="canSeePoField() || SessionUser.canAccessAdmin()"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="po"
|
|
:hover-text="order.po"
|
|
:parse-function="(value) => truncateOrderField(value, 10)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => canSeePoField() || SessionUser.canAccessAdmin()"
|
|
/>
|
|
<td>{{ order.created_at }}</td>
|
|
<td>{{ SessionUser.functions.currency.toLocal(order.total_net_amount) }}</td>
|
|
<!--<td>{{ order.completed_at }}</td>-->
|
|
<td class="is-narrow">
|
|
<div class="buttons pos-order-list-actions">
|
|
<button
|
|
v-if="shouldShowDraftAssignmentActions"
|
|
class="button is-small is-link is-light pos-order-list-assign-customer-button"
|
|
:data-testid="`draft-order-assign-customer-button-${order.id}`"
|
|
@click="openDraftAssignmentModal(order)"
|
|
>
|
|
<span class="icon">
|
|
<i class="fas fa-user-check"></i>
|
|
</span>
|
|
<span>{{ t("admin.pos.drafts_assignment.button") }}</span>
|
|
</button>
|
|
<!-- Attachments -->
|
|
<OrderAttachmentsActionButton
|
|
:order="order"
|
|
:refresh-function="loadList"
|
|
:data-testid="`pos-order-list-attachments-${order.id}`"
|
|
/>
|
|
<ActionSettingsWheelButton
|
|
v-bind:user_id="order.user_id"
|
|
v-bind:order_id="order.id"
|
|
v-bind:invoice_collection_id="order.invoice_collection_id"
|
|
v-bind:reg_1="order.reg_1"
|
|
:refreshFunction="loadList"
|
|
@deleted="loadList()"
|
|
:data-testid="`pos-order-list-settings-${order.id}`"
|
|
>
|
|
<template v-slot:actions>
|
|
<!--
|
|
Go to the order page, in a new tab
|
|
<action-settings-wheel-item
|
|
label="Se transaktion i ny fane"
|
|
icon="fas fa-external-link-alt"
|
|
:click-action="() => redirectDepartmentOrderPage(order.id, order.department_id)"
|
|
:disabled="false"
|
|
/>
|
|
Show the invoice collection, in a new tab
|
|
<action-settings-wheel-item
|
|
label="Vis faktura samling i ny fane"
|
|
icon="fas fa-file-invoice-dollar"
|
|
:click-action="() => redirectSuperUserInvoiceCollectionPage(order.invoice_collection.id)"
|
|
:disabled="!doesOrderHaveInvoiceCollection(order)"
|
|
/>
|
|
-->
|
|
</template>
|
|
</ActionSettingsWheelButton>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<!-- Dropdown content (if the invoice view is enabled,and the dropdown is active) -->
|
|
<tr v-if="props.invoiceView && isDropdownActive(order)">
|
|
<!-- Order line items -->
|
|
<td colspan="100%">
|
|
<!-- Content -->
|
|
<div class="content">
|
|
<OrderContentTable :order-id="order.id" />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="100%">
|
|
{{ t("tables.showing") }} {{ getVisibleOrders.length }}
|
|
{{ SessionUser.objects.orders.meta.labels.entries.toLowerCase() }}
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</template>
|
|
<!-- Treat tablet the same as mobile to ensure visibility on larger phones/tablets -->
|
|
<template #tablet>
|
|
<div class="columns is-multiline is-mobile">
|
|
<!-- Bulk actions when invoice view allows selection -->
|
|
<div class="column is-full" v-if="props.invoiceView && props.allowSelectMultiple">
|
|
<div class="buttons">
|
|
<button
|
|
class="button is-small"
|
|
@click="invoiceSelectedCollections()"
|
|
:disabled="selectedInvoiceCollections.length === 0 || isInvoiceQueueBusy"
|
|
>
|
|
{{ SessionUser.objects.global.language.invoice }}
|
|
{{ SessionUser.objects.global.language.selected_multiple }} ({{ selectedInvoiceCollections.length }})
|
|
</button>
|
|
<button
|
|
class="button is-small"
|
|
@click="selectAllInvoiceCollections()"
|
|
:class="{
|
|
'is-light': !isInvoiceCollectionSelectedAll(),
|
|
'is-dark': isInvoiceCollectionSelectedAll(),
|
|
}"
|
|
>
|
|
{{
|
|
isInvoiceCollectionSelectedAll()
|
|
? SessionUser.objects.global.language.unselect
|
|
: SessionUser.objects.global.language.select
|
|
}}
|
|
{{ SessionUser.objects.global.language.all.toLowerCase() }}
|
|
</button>
|
|
<button
|
|
class="button is-small"
|
|
@click="toggleAutoExpandAll()"
|
|
:class="{
|
|
'is-light': !isAutoExpandAll(),
|
|
'is-dark': isAutoExpandAll(),
|
|
}"
|
|
>
|
|
{{
|
|
isAutoExpandAll()
|
|
? SessionUser.objects.global.language.collapse
|
|
: SessionUser.objects.global.language.expand
|
|
}}
|
|
{{ SessionUser.objects.global.language.all.toLowerCase() }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Orders as cards -->
|
|
<template v-for="order in getVisibleOrders" :key="order.id">
|
|
<div class="column is-full">
|
|
<WhiteBoxCard
|
|
:forceStateFooter="false"
|
|
:forceState="false"
|
|
:defaultOpen="false"
|
|
:toggleable="true"
|
|
class="is-clickable"
|
|
:has-hover-effect="true"
|
|
:hasSelectionStyle="false"
|
|
>
|
|
<template #header>
|
|
<div
|
|
class="card-header-title is-flex is-justify-content-space-between is-align-items-center"
|
|
style="width: 100%"
|
|
>
|
|
<div>
|
|
<div>
|
|
<small>
|
|
<!-- ID and status icon -->
|
|
<span class="icon mr-1" :class="getOrderInvoiceStatusBarColor(order)">
|
|
<i :class="isPendingHandheld(order) ? 'fas fa-mobile-alt' : 'fas fa-circle'"></i>
|
|
</span>
|
|
#{{ order.id }}
|
|
</small>
|
|
</div>
|
|
<div v-if="!props.isCustomerView">
|
|
<small>{{ order.customer_name }}</small>
|
|
</div>
|
|
</div>
|
|
<div class="is-pulled-right" style="width: fit-content">
|
|
<div
|
|
class="columns is-multiline is-mobile is-gapless is-justify-content-flex-end is-align-items-center"
|
|
>
|
|
<div class="column is-narrow has-text-right mb-2">
|
|
<span class="tag is-light is-small mr-1">
|
|
<small>{{ getDepartmentName(order.department_id) }}</small>
|
|
</span>
|
|
</div>
|
|
<div class="column is-12 has-text-right mb-2">
|
|
<span class="buttons is-right mr-1">
|
|
<button
|
|
v-if="props.invoiceView && props.allowSelectMultiple"
|
|
class="button is-light is-small"
|
|
@click="toggleInvoiceCollectionSelection(order.invoice_collection_id)"
|
|
:class="{ 'is-dark': isInvoiceCollectionSelected(parseInt(order.invoice_collection_id)) }"
|
|
>
|
|
{{
|
|
isInvoiceCollectionSelected(parseInt(order.invoice_collection_id))
|
|
? SessionUser.objects.global.language.unselect
|
|
: SessionUser.objects.global.language.select
|
|
}}
|
|
</button>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #default>
|
|
<div class="card-content">
|
|
<div class="columns is-mobile is-multiline">
|
|
<div class="column is-12">
|
|
<small
|
|
>{{ order.reg_1 }}<span v-if="order.reg_2">, {{ order.reg_2 }}</span
|
|
><span v-if="order.reg_3">, {{ order.reg_3 }}</span></small
|
|
>
|
|
</div>
|
|
<div class="column is-12">
|
|
<strong
|
|
><small>{{ order.created_at }}</small></strong
|
|
>
|
|
</div>
|
|
<div class="column is-12">
|
|
<small
|
|
>{{ SessionUser.objects.orders.columns.total_net_amount.label || "Total" }}:
|
|
{{ SessionUser.functions.currency.toLocal(order.total_net_amount) }}</small
|
|
>
|
|
</div>
|
|
<div class="column is-12" v-if="order.completed_at">
|
|
<small
|
|
>{{ SessionUser.objects.orders.columns.completed_at.label }}: {{ order.completed_at }}</small
|
|
>
|
|
</div>
|
|
<div class="column is-12" v-if="isPendingHandheld(order)">
|
|
<small class="has-text-warning"
|
|
>{{ SessionUser.objects.global.language.handheld_pending_order }} •
|
|
{{ SessionUser.objects.global.language.confirmation_needed }}</small
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #content>
|
|
<div>
|
|
<p v-show="!!order.po"><strong>PO:</strong> {{ order.po || "-" }}</p>
|
|
<p v-show="!!order.reference">
|
|
<strong>{{ SessionUser.objects.orders.columns.reference?.label || "Reference" }}:</strong>
|
|
{{ order.reference || "-" }}
|
|
</p>
|
|
<p v-show="!!order.notes">
|
|
<strong>{{ SessionUser.objects.orders.columns.notes?.label || "Noter" }}:</strong>
|
|
{{ order.notes || "-" }}
|
|
</p>
|
|
<hr />
|
|
<!-- Actions and attachments -->
|
|
<div class="buttons is-right">
|
|
<button
|
|
v-if="shouldShowDraftAssignmentActions"
|
|
class="button is-small is-link is-light pos-order-list-assign-customer-button"
|
|
:data-testid="`draft-order-assign-customer-button-${order.id}`"
|
|
@click="openDraftAssignmentModal(order)"
|
|
>
|
|
<span class="icon">
|
|
<i class="fas fa-user-check"></i>
|
|
</span>
|
|
<span>{{ t("admin.pos.drafts_assignment.button") }}</span>
|
|
</button>
|
|
<ActionSettingsWheelButton
|
|
v-bind:user_id="order.user_id"
|
|
v-bind:order_id="order.id"
|
|
v-bind:invoice_collection_id="order.invoice_collection_id"
|
|
v-bind:reg_1="order.reg_1"
|
|
:refreshFunction="loadList"
|
|
@deleted="loadList()"
|
|
:displayActionsDirectly="true"
|
|
>
|
|
<template #actions></template>
|
|
</ActionSettingsWheelButton>
|
|
<OrderAttachmentsActionButton
|
|
:order="order"
|
|
:refresh-function="loadList"
|
|
:data-testid="`pos-order-list-attachments-${order.id}`"
|
|
/>
|
|
</div>
|
|
<!-- Order line items -->
|
|
<div class="content" v-if="props.invoiceView && isAutoExpandAll()">
|
|
<OrderContentTable :order-id="order.id" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</WhiteBoxCard>
|
|
</div>
|
|
</template>
|
|
<!-- Footer count -->
|
|
<div class="column is-full">
|
|
<div class="has-text-centered">
|
|
<small
|
|
>{{ t("tables.showing") }} {{ getVisibleOrders.length }}
|
|
{{ SessionUser.objects.orders.meta.labels.entries.toLowerCase() }}</small
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #mobile>
|
|
<div class="columns is-multiline is-mobile">
|
|
<!-- Bulk actions when invoice view allows selection -->
|
|
<div class="column is-full" v-if="props.invoiceView && props.allowSelectMultiple">
|
|
<div class="buttons">
|
|
<button
|
|
class="button is-small"
|
|
@click="invoiceSelectedCollections()"
|
|
:disabled="selectedInvoiceCollections.length === 0 || isInvoiceQueueBusy"
|
|
>
|
|
{{ SessionUser.objects.global.language.invoice }}
|
|
{{ SessionUser.objects.global.language.selected_multiple }} ({{ selectedInvoiceCollections.length }})
|
|
</button>
|
|
<button
|
|
class="button is-small"
|
|
@click="selectAllInvoiceCollections()"
|
|
:class="{
|
|
'is-light': !isInvoiceCollectionSelectedAll(),
|
|
'is-dark': isInvoiceCollectionSelectedAll(),
|
|
}"
|
|
>
|
|
{{
|
|
isInvoiceCollectionSelectedAll()
|
|
? SessionUser.objects.global.language.unselect
|
|
: SessionUser.objects.global.language.select
|
|
}}
|
|
{{ SessionUser.objects.global.language.all.toLowerCase() }}
|
|
</button>
|
|
<button
|
|
class="button is-small"
|
|
@click="toggleAutoExpandAll()"
|
|
:class="{
|
|
'is-light': !isAutoExpandAll(),
|
|
'is-dark': isAutoExpandAll(),
|
|
}"
|
|
>
|
|
{{
|
|
isAutoExpandAll()
|
|
? SessionUser.objects.global.language.collapse
|
|
: SessionUser.objects.global.language.expand
|
|
}}
|
|
{{ SessionUser.objects.global.language.all.toLowerCase() }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Orders as cards -->
|
|
<template v-for="order in getVisibleOrders" :key="order.id">
|
|
<div class="column is-full">
|
|
<WhiteBoxCard
|
|
:forceStateFooter="true"
|
|
:forceState="false"
|
|
:defaultOpen="false"
|
|
:toggleable="true"
|
|
class="is-clickable pos-orders-mobile-card"
|
|
:has-hover-effect="true"
|
|
:hasSelectionStyle="false"
|
|
:data-testid="`pos-order-list-card-${order.id}`"
|
|
>
|
|
<template #header>
|
|
<div
|
|
class="card-header-title is-flex is-justify-content-space-between is-align-items-center"
|
|
style="width: 100%"
|
|
>
|
|
<div>
|
|
<div>
|
|
<small>
|
|
<!-- ID and status icon -->
|
|
<span class="icon mr-1" :class="getOrderInvoiceStatusBarColor(order)">
|
|
<i :class="isPendingHandheld(order) ? 'fas fa-mobile-alt' : 'fas fa-circle'"></i>
|
|
</span>
|
|
{{ order.reg_1 }}<span v-if="order.reg_2">, {{ order.reg_2 }}</span
|
|
><span v-if="order.reg_3">, {{ order.reg_3 }}</span>
|
|
<span class="has-text-grey-light">#{{ order.id }}</span></small
|
|
>
|
|
</div>
|
|
<div v-if="!props.isCustomerView">
|
|
<small>{{ order.customer_name }}</small>
|
|
</div>
|
|
</div>
|
|
<div class="is-pulled-right" style="width: fit-content">
|
|
<div
|
|
class="columns is-multiline is-mobile is-gapless is-justify-content-flex-end is-align-items-center"
|
|
>
|
|
<div class="column is-narrow has-text-right mb-2">
|
|
<span class="tag is-light is-small mr-1">
|
|
<small>{{ getDepartmentName(order.department_id) }}</small>
|
|
</span>
|
|
</div>
|
|
<div class="column is-12 has-text-right mb-2">
|
|
<span class="buttons is-right mr-1">
|
|
<button
|
|
v-if="props.invoiceView && props.allowSelectMultiple"
|
|
class="button is-light is-small"
|
|
@click="toggleInvoiceCollectionSelection(order.invoice_collection_id)"
|
|
:class="{ 'is-dark': isInvoiceCollectionSelected(parseInt(order.invoice_collection_id)) }"
|
|
>
|
|
{{
|
|
isInvoiceCollectionSelected(parseInt(order.invoice_collection_id))
|
|
? SessionUser.objects.global.language.unselect
|
|
: SessionUser.objects.global.language.select
|
|
}}
|
|
</button>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #default>
|
|
<div class="card-content">
|
|
<div class="columns is-mobile is-multiline">
|
|
<div class="column is-12">
|
|
<template v-if="!!order.reference">
|
|
<div class="columns is-mobile is-gapless is-align-items-center mb-1">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small"
|
|
>{{ SessionUser.objects.orders.columns.reference?.label || "Reference" }}:</label
|
|
>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reference"
|
|
:hover-text="order.reference"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => true"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template v-if="!!order.notes">
|
|
<div class="columns is-mobile is-gapless is-align-items-center mb-1">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small" :class="{ 'has-text-grey': !order.notes }"
|
|
>{{ SessionUser.objects.orders.columns.notes?.label || "Noter" }}:</label
|
|
>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="notes"
|
|
:hover-text="order.notes"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => SessionUser.canAccessAdmin()"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template v-if="!!order.po">
|
|
<div class="columns is-mobile is-gapless is-align-items-center mb-1">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small" :class="{ 'has-text-grey': !order.po }">PO:</label>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="po"
|
|
:hover-text="order.po"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => canSeePoField() || SessionUser.canAccessAdmin()"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<!-- Order summary -->
|
|
<hr v-if="!!order.reference || !!order.notes || !!order.po" />
|
|
<order-content-table
|
|
:order-id="order.id"
|
|
:summaryMode="true"
|
|
:display-price="SessionUser.canAccessAdmin()"
|
|
/>
|
|
</div>
|
|
<div class="column is-12 has-text-centered">
|
|
<hr />
|
|
<!-- Total net amount -->
|
|
<strong>{{ SessionUser.functions.currency.toLocal(order.total_net_amount) }}</strong>
|
|
<br />
|
|
<!-- Cashier • Time ago -->
|
|
<small class="has-text-grey"
|
|
>{{ order.cashier_name }} • {{ SessionUser.functions.date.timeAgo(order.created_at) }}</small
|
|
>
|
|
<!-- Reg 1 • Reg 2 • Reg 3 -->
|
|
<br />
|
|
<div class="has-text-grey">
|
|
<div class="columns is-mobile is-gapless is-justify-content-center is-align-items-center">
|
|
<div class="column is-narrow">
|
|
<template v-if="!!order.reg_1">
|
|
<div class="columns is-mobile is-gapless is-align-items-center">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small">Reg 1:</label>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_1"
|
|
:hover-text="order.reg_1"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => true"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<template v-if="order.reg_2 || order.reg_3">
|
|
<div class="column">
|
|
<div class="columns is-mobile is-gapless is-align-items-center">
|
|
<div class="column">
|
|
<label class="label is-small has-text-grey">•</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="column is-narrow" v-if="order.reg_2">
|
|
<template v-if="!!order.reg_2">
|
|
<div class="columns is-mobile is-gapless is-align-items-center">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small">Reg 2:</label>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_2"
|
|
:hover-text="order.reg_2"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => true"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<template v-if="order.reg_2 && order.reg_3">
|
|
<div class="column">
|
|
<div class="columns is-mobile is-gapless is-align-items-center">
|
|
<div class="column">
|
|
<label class="label is-small has-text-grey">•</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="column is-narrow" v-if="order.reg_3">
|
|
<template v-if="!!order.reg_3">
|
|
<div class="columns is-mobile is-gapless is-align-items-center">
|
|
<div class="column is-narrow mr-1">
|
|
<label class="label is-small">Reg 3:</label>
|
|
</div>
|
|
<div class="column is-flex-grow-1">
|
|
<small>
|
|
<editable-table-column
|
|
:componentWrapper="'div'"
|
|
:object="order"
|
|
:loadList="loadList"
|
|
column="reg_3"
|
|
:hover-text="order.reg_3"
|
|
:parse-function="(value) => truncateOrderField(value, 20)"
|
|
:edit-function="SessionUser.objects.orders.showEditObjectFieldForm"
|
|
:permission-check-function="() => true"
|
|
/>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #content> </template>
|
|
<template #footer>
|
|
<!-- Delete
|
|
<a class="card-footer-item" @click="SessionUser.objects.orders.functions.showDeleteConfirmationModal(order, loadList)">
|
|
{{ SessionUser.objects.global.language.delete }}
|
|
</a>-->
|
|
<a
|
|
class="card-footer-item"
|
|
@click="openOrderActionsModal(order)"
|
|
:data-testid="`pos-order-list-actions-${order.id}`"
|
|
>
|
|
<span class="icon mr-1">
|
|
<i class="fas fa-cog"></i>
|
|
</span>
|
|
</a>
|
|
<!-- Go to booking (if any) -->
|
|
<a
|
|
class="card-footer-item"
|
|
v-if="order.booking_id"
|
|
@click="redirectDepartmentBookingPage(order.booking_id, order.department_id, false)"
|
|
>
|
|
<span class="icon mr-1">
|
|
<i class="fas fa-calendar-alt"></i>
|
|
</span>
|
|
<span>{{ SessionUser.objects.order_bookings.meta.labels.single }}</span>
|
|
</a>
|
|
<!-- Go to order page -->
|
|
<a class="card-footer-item" @click="redirectDepartmentOrderPage(order.id, order.department_id, false)">
|
|
<span class="icon mr-1">
|
|
<i class="fas fa-arrow-right"></i>
|
|
</span>
|
|
</a>
|
|
</template>
|
|
</WhiteBoxCard>
|
|
</div>
|
|
</template>
|
|
<!-- Footer count -->
|
|
<div class="column is-full">
|
|
<div class="has-text-centered">
|
|
<small
|
|
>{{ t("tables.showing") }} {{ getVisibleOrders.length }}
|
|
{{ SessionUser.objects.orders.meta.labels.entries.toLowerCase() }}</small
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</ViewportResponsiveWrapper>
|
|
<InvoiceMultipleCollectionsModal
|
|
v-if="isInvoiceMultipleCollectionsModalOpen"
|
|
v-bind:in-progress="invoiceCollectionQueueInProgress"
|
|
v-bind:failed="invoiceCollectionQueueFailed"
|
|
v-bind:succeeded="invoiceCollectionQueueSuccess"
|
|
v-bind:queued="invoiceCollectionQueue"
|
|
v-bind:jobs="invoiceQueue.invoiceCollectionQueueJobs"
|
|
v-bind:log="collectionQueueLog"
|
|
@closeModal="confirmCloseModal()"
|
|
@retry="retryInvoiceCollection"
|
|
/>
|
|
<!-- bottom progress bar -->
|
|
<InvoicingBillingPeriodInvoiceProgressBar
|
|
v-bind:max-progress="
|
|
invoiceCollectionQueueSuccess.length +
|
|
invoiceCollectionQueueFailed.length +
|
|
invoiceCollectionQueueInProgress.length +
|
|
invoiceCollectionQueue.length
|
|
"
|
|
v-bind:progress="invoiceCollectionQueueSuccess.length"
|
|
v-bind:failed="invoiceCollectionQueueFailed.length"
|
|
v-bind:in-progress="invoiceCollectionQueueInProgress.length"
|
|
v-bind:queued="invoiceCollectionQueue.length"
|
|
@dismissed="isInvoiceMultipleCollectionsModalOpen = true"
|
|
/>
|
|
<!-- Order actions menu modal -->
|
|
<div
|
|
class="modal"
|
|
:class="{ 'is-active': isOrderActionsModalOpen }"
|
|
v-if="selectedOrderForActionsMenu"
|
|
data-testid="pos-order-actions-modal"
|
|
>
|
|
<div class="modal-background" @click="closeOrderActionsModal"></div>
|
|
<div class="modal-card">
|
|
<header class="modal-card-head">
|
|
<p class="modal-card-title">{{ SessionUser.objects.global.language.actions }}</p>
|
|
<button class="delete" aria-label="close" @click="closeOrderActionsModal"></button>
|
|
</header>
|
|
<section class="modal-card-body">
|
|
<button
|
|
v-if="shouldShowDraftAssignmentActions && selectedOrderForActionsMenu"
|
|
class="button is-link is-light is-fullwidth mb-4"
|
|
:data-testid="`draft-order-assign-customer-modal-button-${selectedOrderForActionsMenu.id}`"
|
|
@click="openDraftAssignmentModal(selectedOrderForActionsMenu)"
|
|
>
|
|
<span class="icon">
|
|
<i class="fas fa-user-check"></i>
|
|
</span>
|
|
<span>{{ t("admin.pos.drafts_assignment.button") }}</span>
|
|
</button>
|
|
<ActionSettingsWheelButton
|
|
v-bind:user_id="selectedOrderForActionsMenu.user_id"
|
|
v-bind:order_id="selectedOrderForActionsMenu.id"
|
|
v-bind:invoice_collection_id="selectedOrderForActionsMenu.invoice_collection_id"
|
|
v-bind:reg_1="selectedOrderForActionsMenu.reg_1"
|
|
v-bind:reg_2="selectedOrderForActionsMenu.reg_2"
|
|
v-bind:reg_3="selectedOrderForActionsMenu.reg_3"
|
|
v-bind:order_booking_id="selectedOrderForActionsMenu.booking_id"
|
|
v-bind:department_id="selectedOrderForActionsMenu.department_id"
|
|
:refreshFunction="loadList"
|
|
@deleted="loadList()"
|
|
:displayActionsDirectly="true"
|
|
>
|
|
<template #actions></template>
|
|
</ActionSettingsWheelButton>
|
|
</section>
|
|
<footer class="modal-card-foot">
|
|
<button class="button is-light is-link is-fullwidth" @click="closeOrderActionsModal">
|
|
{{ SessionUser.objects.global.language.close }}
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
<AssignDraftOrderCustomerModal
|
|
v-if="isDraftAssignmentModalOpen && selectedOrderForDraftAssignment"
|
|
:order="selectedOrderForDraftAssignment"
|
|
@close="closeDraftAssignmentModal"
|
|
@assigned="handleDraftAssignmentSuccess"
|
|
/>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.status-bar-table-header {
|
|
max-width: 5px;
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
.status-bar-table-row {
|
|
border-radius: 5px;
|
|
width: 5px;
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
.status-bar.has-text-danger {
|
|
border-color: #ff3860;
|
|
}
|
|
.status-bar.has-text-warning {
|
|
border-color: #ffdd57;
|
|
}
|
|
.status-bar.has-text-success {
|
|
border-color: #48c774;
|
|
}
|
|
|
|
.buttons.pos-order-list-actions {
|
|
flex-wrap: nowrap;
|
|
justify-content: flex-end;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.buttons.pos-order-list-actions > * {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.pos-order-list-assign-customer-button {
|
|
border-radius: 0.8rem;
|
|
padding-inline: 0.85rem;
|
|
}
|
|
|
|
@media screen and (max-width: 768px) {
|
|
.pos-orders-mobile-card {
|
|
border: 1px solid #d7dee8;
|
|
border-radius: 0.9rem;
|
|
box-shadow: 0 10px 24px rgba(19, 35, 57, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.pos-orders-mobile-card :deep(.card) {
|
|
background: #ffffff;
|
|
}
|
|
|
|
.pos-orders-mobile-card :deep(.card-header) {
|
|
border-bottom: 1px solid #e3e9f2;
|
|
}
|
|
|
|
.pos-orders-mobile-card :deep(.card-content) {
|
|
border-top: 1px solid #eef2f7;
|
|
}
|
|
|
|
.pos-orders-mobile-card :deep(.card-footer) {
|
|
border-top: 1px solid #e3e9f2;
|
|
background: #fbfcfe;
|
|
}
|
|
}
|
|
</style>
|