Files
pleno-vue/src/components/displays/superuser/tables/OrderContentTable.vue
T

341 lines
12 KiB
Vue

<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
import { computed, ref, watch } from "vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import InvoicingPeriodFlagList from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/components/InvoicingPeriodFlagList.vue";
const props = defineProps({
orderId: {
type: Number,
required: true,
},
displayPrice: {
type: Boolean,
default: true,
},
displayReference: {
type: Boolean,
default: true,
},
displayNotes: {
type: Boolean,
default: true,
},
useLocalOrderItems: {
type: Boolean,
default: false,
},
localOrderItems: {
type: Array,
default: () => [],
},
summaryMode: {
type: Boolean,
default: false,
},
invoicePeriodFlags: {
type: Array,
default: () => [],
},
})
const emit = defineEmits(["flagStatusChanged"]);
const order_item_default = {
"id": null,
"order_id": null,
"product_id": null,
"reference": null,
"notes": null,
"cashier_id": null,
"price": null,
"quantity": null,
"related_item_id": null,
"product": {
"id": null,
"name": null,
"description": null,
"price": null,
"category": null,
"piktogram": null,
"economic_product_id": null,
"apply_category_discount": null,
"requires_note": null,
"created_at": null,
"updated_at": null
}
};
const orderItems = ref([]);
const loadOrderItems = async () => {
if (props.useLocalOrderItems) {
orderItems.value = props.localOrderItems;
return;
}
await SessionUser.request(
'/order/items',
'GET',
{
order_id: props.orderId,
}
).then((response) => {
orderItems.value = response.data.data;
}).catch((error) => {
console.error(error);
});
}
const isOrderItemAddon = (item) => {
return item.related_item_id !== 0;
}
const getItemAddons = (item) => {
return orderItems.value.filter((order_item) => {
return order_item.related_item_id === item.id;
});
}
const ucFirst = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const sortInvoicePeriodFlags = (flags = []) => [...flags].sort((left, right) => {
const sourceOrder = { manual: 0, automatic: 1 };
const leftSource = sourceOrder[left?.source] ?? 99;
const rightSource = sourceOrder[right?.source] ?? 99;
if (leftSource !== rightSource) {
return leftSource - rightSource;
}
return String(left?.created_at || left?.fingerprint || "").localeCompare(
String(right?.created_at || right?.fingerprint || "")
);
});
const getOrderItemFlags = (orderItem) => sortInvoicePeriodFlags((props.invoicePeriodFlags || []).filter((flag) => {
if (String(flag?.status || "active") !== "active") {
return false;
}
const targetType = String(flag?.target_type || "");
if (!["order_item", "order_item_field"].includes(targetType)) {
return false;
}
const flagOrderItemId = Number(flag?.order_item_id || flag?.target_id || flag?.context?.order_item_id || 0);
return flagOrderItemId > 0 && flagOrderItemId === Number(orderItem?.id || 0);
}));
const getOrderItemFlagIconClass = (orderItem) => (
getOrderItemFlags(orderItem).length > 0 ? "fas fa-flag" : ""
);
const getOrderItemFlagColorClass = (orderItem) => {
const flags = getOrderItemFlags(orderItem);
if (flags.length === 0) {
return "";
}
return flags.some((flag) => flag?.source === "manual") ? "has-text-danger" : "has-text-warning";
};
const displayColumnCount = computed(() => (
2 + (props.displayNotes ? 1 : 0) + (props.displayReference ? 1 : 0) + (props.displayPrice ? 1 : 0)
));
const emitFlagStatusChanged = (flag) => emit("flagStatusChanged", flag);
loadOrderItems();
watch(() => props.orderId, (newValue, oldValue) => {
if (newValue !== oldValue) {
loadOrderItems();
}
}, { immediate: true });
</script>
<template>
<div>
<table class="table is-fullwidth" v-if="!props.summaryMode">
<thead>
<tr>
<th>{{ $t('tables.products.name') }}</th>
<th v-if="props.displayNotes">{{ $t('objects.columns.notes') }}</th>
<th v-if="props.displayReference">{{ $t('common.reference') }}</th>
<th>{{ $t('common.quantity') }}</th>
<th v-if="props.displayPrice">{{ $t('tables.products.price') }}</th>
</tr>
</thead>
<tbody>
<template v-for="item in orderItems" :key="item.id">
<!-- If the item is a primary item, show it -->
<template v-if="!isOrderItemAddon(item)">
<!-- If the item is included in the invoice, show it normally -->
<tr v-if="item.include_in_invoice">
<td>
<span
v-if="getOrderItemFlags(item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(item)"
:data-testid="`order-content-item-flag-indicator-${item.id}`"
>
<i :class="getOrderItemFlagIconClass(item)"></i>
</span>
{{ item.product.name }}
</td>
<td v-if="props.displayReference">{{ item.reference }}</td>
<td v-if="props.displayNotes">{{ item.notes }}</td>
<td>{{ item.quantity }}</td>
<td v-if="props.displayPrice">{{ SessionUser.functions.currency.toLocal(item.price) }}</td>
</tr>
<!-- If the item is not included in the invoice, show it with a strikethrough -->
<tr v-else class="has-background-warning-light">
<td>
<span
v-if="getOrderItemFlags(item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(item)"
:data-testid="`order-content-item-flag-indicator-${item.id}`"
>
<i :class="getOrderItemFlagIconClass(item)"></i>
</span>
<span style="text-decoration: line-through;">{{ item.product.name }}</span> ( {{SessionUser.objects.vehicles.columns.wash_subscription.label }} )
</td>
<td v-if="props.displayReference">{{ item.reference }}</td>
<td v-if="props.displayNotes">{{ item.notes }}</td>
<td>{{ item.quantity }}</td>
<td v-if="props.displayPrice" style="text-decoration: line-through;">{{ SessionUser.functions.currency.toLocal(item.price) }}</td>
</tr>
<tr
v-if="getOrderItemFlags(item).length > 0"
:data-testid="`order-content-item-flags-${item.id}`"
>
<td :colspan="displayColumnCount">
<InvoicingPeriodFlagList
compact
:flags="getOrderItemFlags(item)"
@statusChanged="emitFlagStatusChanged"
/>
</td>
</tr>
</template>
<!-- Show the add-on items -->
<template v-for="addon_item in getItemAddons(item)" :key="addon_item.id">
<!-- If the add-on item is included in the invoice, show it normally -->
<tr v-if="addon_item.include_in_invoice">
<td>
<span
v-if="getOrderItemFlags(addon_item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(addon_item)"
:data-testid="`order-content-item-flag-indicator-${addon_item.id}`"
>
<i :class="getOrderItemFlagIconClass(addon_item)"></i>
</span>
+ {{ addon_item.product.name }}
</td>
<td v-if="props.displayReference">{{ addon_item.reference }}</td>
<td v-if="props.displayNotes">{{ addon_item.notes }}</td>
<td>{{ addon_item.quantity }}</td>
<td v-if="props.displayPrice">{{ SessionUser.functions.currency.toLocal(addon_item.price) }}</td>
</tr>
<!-- If the add-on item is not included in the invoice, show it with a strikethrough -->
<tr v-else class="has-background-warning">
<td>
<span
v-if="getOrderItemFlags(addon_item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(addon_item)"
:data-testid="`order-content-item-flag-indicator-${addon_item.id}`"
>
<i :class="getOrderItemFlagIconClass(addon_item)"></i>
</span>
+ <span style="text-decoration: line-through;">{{ addon_item.product.name }}</span> ( {{SessionUser.objects.vehicles.columns.wash_subscription.label }} )
</td>
<td v-if="props.displayReference">{{ addon_item.reference }}</td>
<td v-if="props.displayNotes">{{ addon_item.notes }}</td>
<td>{{ addon_item.quantity }}</td>
<td v-if="props.displayPrice" style="text-decoration: line-through;">{{ SessionUser.functions.currency.toLocal(addon_item.price) }}</td>
</tr>
<tr
v-if="getOrderItemFlags(addon_item).length > 0"
:data-testid="`order-content-item-flags-${addon_item.id}`"
>
<td :colspan="displayColumnCount">
<InvoicingPeriodFlagList
compact
:flags="getOrderItemFlags(addon_item)"
@statusChanged="emitFlagStatusChanged"
/>
</td>
</tr>
</template>
</template>
</tbody>
</table>
<!-- Summary Mode -->
<div v-else>
<!-- Loader -->
<template v-if="orderItems.length === 0">
<span class="is-flex is-justify-content-center is-align-items-center" style="height: 100px;">
<span class="loader is-loading is-align-self-center"></span>
</span>
</template>
<!-- Order Items -->
<template v-else>
<div v-for="item in orderItems" :key="item.id" class="mb-2">
<!-- If the item is a primary item, show it -->
<template v-if="!isOrderItemAddon(item)">
<div>
<span
v-if="getOrderItemFlags(item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(item)"
:data-testid="`order-content-item-flag-indicator-${item.id}`"
>
<i :class="getOrderItemFlagIconClass(item)"></i>
</span>
<strong>{{ item.product.name }}</strong> x {{ item.quantity }}
<span v-if="props.displayPrice"> - {{ SessionUser.functions.currency.toLocal(item.price) }}</span>
</div>
<InvoicingPeriodFlagList
compact
:flags="getOrderItemFlags(item)"
@statusChanged="emitFlagStatusChanged"
/>
<!-- Show the add-on items -->
<div v-for="addon_item in getItemAddons(item)" :key="addon_item.id" class="ml-4">
<small>
<span
v-if="getOrderItemFlags(addon_item).length > 0"
class="icon is-small mr-1 invoice-period-item-flag-indicator"
:class="getOrderItemFlagColorClass(addon_item)"
:data-testid="`order-content-item-flag-indicator-${addon_item.id}`"
>
<i :class="getOrderItemFlagIconClass(addon_item)"></i>
</span>
+ {{ addon_item.product.name }} x {{ addon_item.quantity }}
<span v-if="props.displayPrice"> - {{ SessionUser.functions.currency.toLocal(addon_item.price) }}</span>
</small>
<InvoicingPeriodFlagList
compact
:flags="getOrderItemFlags(addon_item)"
@statusChanged="emitFlagStatusChanged"
/>
</div>
</template>
</div>
</template>
</div>
</div>
</template>
<style scoped>
.invoice-period-item-flag-indicator {
vertical-align: middle;
}
</style>