188 lines
6.0 KiB
Vue
188 lines
6.0 KiB
Vue
<template>
|
|
<el-card v-if="hasManualLines" shadow="never">
|
|
<template #header>
|
|
<span>Manual shipping payment</span>
|
|
</template>
|
|
|
|
<template v-if="canSetDeliveryCost">
|
|
<div class="mb-16">
|
|
<el-alert
|
|
type="warning"
|
|
title="Shipping quote required"
|
|
description="Publish a shipping quote to create the delivery payment session for the buyer. Until then, they cannot pay for shipping and the order cannot be fulfilled."
|
|
:closable="false"
|
|
show-icon
|
|
/>
|
|
</div>
|
|
|
|
<el-form
|
|
ref="deliveryCostFormRef"
|
|
label-position="top"
|
|
:model="deliveryCostForm"
|
|
:rules="deliveryCostRules"
|
|
>
|
|
<el-form-item :label="`Shipping cost (${order.fiatCurrency})`" prop="deliveryCost">
|
|
<div class="w-full">
|
|
<el-input-number
|
|
v-model="deliveryCostForm.deliveryCost"
|
|
:min="0"
|
|
:precision="2"
|
|
:step="1"
|
|
class="w-full"
|
|
@update:model-value="deliveryCostFormRef?.clearValidate('deliveryCost')"
|
|
/>
|
|
<p class="secondary-text m-0">Set 0 for free shipping.</p>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-button type="primary" :loading="deliveryCostSaving" @click="submitDeliveryCost">
|
|
Publish shipping quote
|
|
</el-button>
|
|
</el-form>
|
|
</template>
|
|
|
|
<template v-else-if="quoted">
|
|
<el-descriptions :column="1" class="mb-16 descriptions-row-labels">
|
|
<el-descriptions-item label="Shipping cost">
|
|
{{ formatFiatPrice(order.totals.shippingCostFiat ?? 0, order.fiatCurrency) }}
|
|
</el-descriptions-item>
|
|
|
|
<el-descriptions-item v-if="order.shippingInvoice" label="Payment expires">
|
|
{{ formatDate(order.shippingInvoice.expiresAt) }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
|
|
<template v-if="order.shippingInvoice">
|
|
<order-invoice-payment-panel
|
|
:invoice="order.shippingInvoice"
|
|
rate-label="quote"
|
|
/>
|
|
</template>
|
|
|
|
<p v-else class="secondary-text m-0">Free shipping — no payment required.</p>
|
|
</template>
|
|
|
|
<el-alert
|
|
v-else-if="order.failureReason"
|
|
type="error"
|
|
title="Shipping quote unavailable"
|
|
:description="failureReasonDescription"
|
|
:closable="false"
|
|
show-icon
|
|
/>
|
|
</el-card>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, reactive, ref, type PropType } from 'vue';
|
|
import type { FormInstance, FormRules } from 'element-plus';
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
import { useOrdersStore } from '@/stores/orders';
|
|
import type { OrderExtended } from '@/types/order/OrderExtended';
|
|
import { DeliveryMode } from '@/types/product/DeliveryMode';
|
|
import { formatDate } from '@/utils/formatDate';
|
|
import { formatFiatPrice } from '@/utils/formatFiatPrice';
|
|
import { formatOrderFailureReason } from '@/utils/order/formatOrderFailureReason';
|
|
import { isSet } from '@/utils/isSet';
|
|
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
|
|
|
|
const props = defineProps({
|
|
order: {
|
|
type: Object as PropType<OrderExtended>,
|
|
required: true
|
|
}
|
|
});
|
|
|
|
const { setDeliveryCost } = useOrdersStore();
|
|
|
|
const deliveryCostSaving = ref(false);
|
|
const deliveryCostFormRef = ref<FormInstance>();
|
|
|
|
const deliveryCostForm = reactive({
|
|
deliveryCost: 0
|
|
});
|
|
|
|
const quoted = computed(() => isSet(props.order.quotedAt));
|
|
|
|
const hasManualLines = computed(() =>
|
|
(props.order.lines ?? []).some(line => line.deliveryMode === DeliveryMode.Manual)
|
|
);
|
|
|
|
const canSetDeliveryCost = computed(() => !props.order.failureReason && !quoted.value && hasManualLines.value);
|
|
|
|
const failureReasonDescription = computed(() => {
|
|
if (!props.order.failureReason) {
|
|
return '';
|
|
}
|
|
|
|
const reason = formatOrderFailureReason(props.order.failureReason);
|
|
|
|
return `This order cannot be fulfilled (${reason}). A shipping quote cannot be published.`;
|
|
});
|
|
|
|
const deliveryCostRules = computed<FormRules>(() => ({
|
|
deliveryCost: [
|
|
{
|
|
required: true,
|
|
message: 'Shipping cost is required',
|
|
trigger: 'change'
|
|
}
|
|
]
|
|
}));
|
|
|
|
const submitDeliveryCost = async () => {
|
|
const formEl = deliveryCostFormRef.value;
|
|
|
|
if (!formEl) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await formEl.validate();
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
const deliveryCost = deliveryCostForm.deliveryCost;
|
|
|
|
const formattedCost = formatFiatPrice(deliveryCost, props.order.fiatCurrency);
|
|
|
|
const buyerImpactNote =
|
|
deliveryCost > 0
|
|
? 'This will create a payment session for the buyer.'
|
|
: 'This will publish the quote for the buyer; no payment is required for free shipping.';
|
|
|
|
try {
|
|
await ElMessageBox.confirm(
|
|
`You are about to set the shipping quote to ${formattedCost}. ${buyerImpactNote} This cannot be undone. Ensure the quote is correct.`,
|
|
'Publish shipping quote',
|
|
{
|
|
type: 'warning',
|
|
confirmButtonText: 'Publish',
|
|
cancelButtonText: 'Cancel'
|
|
}
|
|
);
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
deliveryCostSaving.value = true;
|
|
|
|
try {
|
|
await setDeliveryCost(props.order.id, {
|
|
deliveryCost
|
|
});
|
|
|
|
ElMessage.success('Shipping quote published.');
|
|
} catch (error) {
|
|
const fallback = 'Could not publish shipping quote.';
|
|
|
|
const message = resolveAxiosErrorMessage(error, fallback);
|
|
|
|
ElMessage.error(message);
|
|
} finally {
|
|
deliveryCostSaving.value = false;
|
|
}
|
|
};
|
|
</script>
|