feat: require payment method when submitting a preorder
Price depends on cash or check terms, so the customer chooses them before admin pricing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { SettlementType } from '@/types'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
settlementType: SettlementType | null
|
||||||
|
checkTermMonths: number | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:settlementType': [value: SettlementType]
|
||||||
|
'update:checkTermMonths': [value: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const methods: { value: SettlementType; title: string; hint: string }[] = [
|
||||||
|
{ value: 'cash', title: 'نقدی', hint: 'کل مبلغ بهصورت نقدی' },
|
||||||
|
{ value: 'cash_check', title: 'نصف نقد، نصف چکی', hint: 'نیمی نقد و نیمی چک' },
|
||||||
|
{ value: 'check', title: 'چکی', hint: 'کل مبلغ با چک' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const terms = [1, 2, 3, 4]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<fieldset class="pay-plan">
|
||||||
|
<legend class="pay-plan__legend">روش پرداخت</legend>
|
||||||
|
<p class="pay-plan__note">قیمت پیشسفارش به روش پرداخت بستگی دارد. قبل از اعلام قیمت باید مشخص شود.</p>
|
||||||
|
<div class="pay-plan__methods">
|
||||||
|
<button
|
||||||
|
v-for="method in methods"
|
||||||
|
:key="method.value"
|
||||||
|
type="button"
|
||||||
|
class="pay-plan__method"
|
||||||
|
:class="{ 'pay-plan__method--active': settlementType === method.value }"
|
||||||
|
@click="emit('update:settlementType', method.value)"
|
||||||
|
>
|
||||||
|
<strong>{{ method.title }}</strong>
|
||||||
|
<span>{{ method.hint }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="settlementType === 'check' || settlementType === 'cash_check'" class="pay-plan__terms">
|
||||||
|
<span class="pay-plan__terms-label">مدت چک</span>
|
||||||
|
<div class="pay-plan__terms-row">
|
||||||
|
<button
|
||||||
|
v-for="month in terms"
|
||||||
|
:key="month"
|
||||||
|
type="button"
|
||||||
|
class="pay-plan__term"
|
||||||
|
:class="{ 'pay-plan__term--active': checkTermMonths === month }"
|
||||||
|
@click="emit('update:checkTermMonths', month)"
|
||||||
|
>
|
||||||
|
{{ month }} ماهه
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pay-plan {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__legend {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__note {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-text-secondary, #667085);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__methods,
|
||||||
|
.pay-plan__terms-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__method,
|
||||||
|
.pay-plan__term {
|
||||||
|
text-align: right;
|
||||||
|
border: 1px solid var(--color-border, #e4e7ec);
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__method strong {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__method span {
|
||||||
|
color: var(--color-text-secondary, #667085);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__method--active,
|
||||||
|
.pay-plan__term--active {
|
||||||
|
border-color: var(--color-primary, #1570ef);
|
||||||
|
background: color-mix(in srgb, var(--color-primary, #1570ef) 8%, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__terms-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__terms-row {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-plan__term {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 72px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { useCartStore } from '@/stores/cart'
|
import { useCartStore } from '@/stores/cart'
|
||||||
import { useOrderStore } from '@/stores/order'
|
import { useOrderStore } from '@/stores/order'
|
||||||
import { confirm } from '@/composables/useConfirm'
|
import { confirm } from '@/composables/useConfirm'
|
||||||
|
import type { SettlementType } from '@/types'
|
||||||
|
|
||||||
export function useCartPage() {
|
export function useCartPage() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -11,6 +12,8 @@ export function useCartPage() {
|
|||||||
const showFullDialog = ref(false)
|
const showFullDialog = ref(false)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const submitError = ref('')
|
const submitError = ref('')
|
||||||
|
const settlementType = ref<SettlementType | null>(null)
|
||||||
|
const checkTermMonths = ref<number | null>(null)
|
||||||
|
|
||||||
const items = computed(() => cartStore.activeCart?.items ?? [])
|
const items = computed(() => cartStore.activeCart?.items ?? [])
|
||||||
const isEmpty = computed(() => items.value.length === 0)
|
const isEmpty = computed(() => items.value.length === 0)
|
||||||
@@ -33,11 +36,22 @@ export function useCartPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ثبت سفارش بدون آدرس و بدون انتخاب تسویه.
|
* پیشسفارش با روش پرداخت (و مدت چک در صورت نیاز) ثبت میشود.
|
||||||
* سفارش هنوز نهایی نیست؛ قیمتگذاری و روش تسویه بعد از بررسی مدیریت است.
|
* قیمتگذاری مدیریت بعد از این انتخاب انجام میشود.
|
||||||
*/
|
*/
|
||||||
async function submitOrder() {
|
async function submitOrder() {
|
||||||
if (!cartStore.activeCart || items.value.length === 0 || submitting.value) return
|
if (!cartStore.activeCart || items.value.length === 0 || submitting.value) return
|
||||||
|
if (!settlementType.value) {
|
||||||
|
submitError.value = 'قبل از ثبت پیشسفارش روش پرداخت را انتخاب کنید.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(settlementType.value === 'check' || settlementType.value === 'cash_check') &&
|
||||||
|
!checkTermMonths.value
|
||||||
|
) {
|
||||||
|
submitError.value = 'مدت چک را از ۱ تا ۴ ماه انتخاب کنید.'
|
||||||
|
return
|
||||||
|
}
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
submitError.value = ''
|
submitError.value = ''
|
||||||
try {
|
try {
|
||||||
@@ -46,7 +60,10 @@ export function useCartPage() {
|
|||||||
submitError.value = 'سبد خرید خالی است یا با سرور همگام نشد.'
|
submitError.value = 'سبد خرید خالی است یا با سرور همگام نشد.'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const order = await orderStore.submitOrder(cartStore.activeCart)
|
const order = await orderStore.submitOrder(cartStore.activeCart, {
|
||||||
|
settlementType: settlementType.value,
|
||||||
|
checkTermMonths: checkTermMonths.value,
|
||||||
|
})
|
||||||
await cartStore.hydrate()
|
await cartStore.hydrate()
|
||||||
router.replace(`/orders/${order.id}/success`)
|
router.replace(`/orders/${order.id}/success`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -90,6 +107,8 @@ export function useCartPage() {
|
|||||||
showFullDialog,
|
showFullDialog,
|
||||||
submitting,
|
submitting,
|
||||||
submitError,
|
submitError,
|
||||||
|
settlementType,
|
||||||
|
checkTermMonths,
|
||||||
removeItem,
|
removeItem,
|
||||||
proceedToCheckout,
|
proceedToCheckout,
|
||||||
proceedAnyway,
|
proceedAnyway,
|
||||||
|
|||||||
+31
-4
@@ -117,11 +117,13 @@ export const useOrderStore = defineStore('order', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* سفارش بدون روش تسویه ثبت میشود؛ مشتری بعد از قیمتگذاری مدیریت
|
* پیشسفارش همراه روش پرداخت ثبت میشود تا قیمتگذاری بر همان اساس انجام شود.
|
||||||
* روش تسویه را انتخاب میکند.
|
|
||||||
* اقلام همه شرکتها در یک فاکتور واحد ثبت میشوند.
|
* اقلام همه شرکتها در یک فاکتور واحد ثبت میشوند.
|
||||||
*/
|
*/
|
||||||
async function submitOrder(cart: Cart): Promise<Order> {
|
async function submitOrder(
|
||||||
|
cart: Cart,
|
||||||
|
payment: { settlementType: SettlementType; checkTermMonths?: number | null },
|
||||||
|
): Promise<Order> {
|
||||||
if (!cart.items.length) {
|
if (!cart.items.length) {
|
||||||
throw new Error('سبد خرید خالی است.')
|
throw new Error('سبد خرید خالی است.')
|
||||||
}
|
}
|
||||||
@@ -143,6 +145,9 @@ export const useOrderStore = defineStore('order', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
companyId: companyIds[0],
|
companyId: companyIds[0],
|
||||||
|
settlementType: payment.settlementType,
|
||||||
|
checkTermMonths:
|
||||||
|
payment.settlementType === 'cash' ? undefined : payment.checkTermMonths ?? undefined,
|
||||||
notes: notes || undefined,
|
notes: notes || undefined,
|
||||||
items: cart.items.map((item) => ({
|
items: cart.items.map((item) => ({
|
||||||
productId: item.productId,
|
productId: item.productId,
|
||||||
@@ -159,7 +164,28 @@ export const useOrderStore = defineStore('order', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** انتخاب روش تسویه پس از صدور فاکتور. */
|
async function setPaymentPlan(
|
||||||
|
orderId: string,
|
||||||
|
settlementType: SettlementType,
|
||||||
|
checkTermMonths?: number | null,
|
||||||
|
): Promise<Order> {
|
||||||
|
try {
|
||||||
|
const order = await apiRequest<Order>(`/orders/${orderId}/payment-plan`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
settlementType,
|
||||||
|
checkTermMonths:
|
||||||
|
settlementType === 'cash' ? undefined : checkTermMonths ?? undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
upsertOrder(order)
|
||||||
|
return order
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e instanceof ApiError ? e.message : 'ثبت روش پرداخت ناموفق بود.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** انتخاب روش تسویه پس از صدور فاکتور؛ فقط برای سفارشهایی که روش پرداخت ندارند. */
|
||||||
async function chooseSettlement(
|
async function chooseSettlement(
|
||||||
orderId: string,
|
orderId: string,
|
||||||
settlementType: SettlementType,
|
settlementType: SettlementType,
|
||||||
@@ -273,6 +299,7 @@ export const useOrderStore = defineStore('order', () => {
|
|||||||
removeDraftCheck,
|
removeDraftCheck,
|
||||||
getDraftRass,
|
getDraftRass,
|
||||||
submitOrder,
|
submitOrder,
|
||||||
|
setPaymentPlan,
|
||||||
chooseSettlement,
|
chooseSettlement,
|
||||||
submitCashReceipt,
|
submitCashReceipt,
|
||||||
updateOrderChecks,
|
updateOrderChecks,
|
||||||
|
|||||||
+3
-1
@@ -187,8 +187,10 @@ export interface Order {
|
|||||||
companyName: string
|
companyName: string
|
||||||
items: OrderItem[]
|
items: OrderItem[]
|
||||||
totalMeterage: number
|
totalMeterage: number
|
||||||
/** تا وقتی مشتری بعد از قیمتگذاری روش تسویه را انتخاب نکرده، خالی است. */
|
/** هنگام ثبت پیشسفارش انتخاب میشود. سفارشهای قدیمی یا ثبتشده توسط ادمین ممکن است خالی باشند. */
|
||||||
settlementType: SettlementType | null
|
settlementType: SettlementType | null
|
||||||
|
/** مدت چک به ماه (۱ تا ۴) برای روشهای چکی */
|
||||||
|
checkTermMonths?: number | null
|
||||||
checkMode?: CheckRegistrationMode
|
checkMode?: CheckRegistrationMode
|
||||||
checks?: CheckData[]
|
checks?: CheckData[]
|
||||||
checkRassDays?: number
|
checkRassDays?: number
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ const STATUS_HERO: Partial<Record<OrderStatus, StatusHeroConfig>> = {
|
|||||||
variant: 'waiting',
|
variant: 'waiting',
|
||||||
title: 'پیشسفارش ثبت شد',
|
title: 'پیشسفارش ثبت شد',
|
||||||
description:
|
description:
|
||||||
'سفارش شما برای بررسی و قیمتگذاری به مدیریت ارسال شد. پس از صدور فاکتور میتوانید روش تسویه (نقد یا چک) را انتخاب کنید.',
|
'سفارش شما برای بررسی و قیمتگذاری به مدیریت ارسال شد. روش پرداخت هنگام ثبت پیشسفارش مشخص شده است.',
|
||||||
},
|
},
|
||||||
awaiting_review: {
|
awaiting_review: {
|
||||||
variant: 'waiting',
|
variant: 'waiting',
|
||||||
title: 'در انتظار بررسی مدیریت',
|
title: 'در انتظار بررسی مدیریت',
|
||||||
description:
|
description:
|
||||||
'پیشسفارش شما در صف بررسی است. پس از قیمتگذاری و صدور فاکتور، از طریق همین صفحه برای انتخاب تسویه مطلع میشوید.',
|
'پیشسفارش شما در صف بررسی است. قیمت بر اساس روش پرداختی که انتخاب کردهاید اعلام میشود.',
|
||||||
},
|
},
|
||||||
pricing: {
|
pricing: {
|
||||||
variant: 'waiting',
|
variant: 'waiting',
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import type { OrderStatus, SettlementType } from '@/types'
|
import type { OrderStatus, SettlementType } from '@/types'
|
||||||
|
|
||||||
export const SETTLEMENT_TYPE_LABELS: Record<SettlementType, string> = {
|
export const SETTLEMENT_TYPE_LABELS: Record<SettlementType, string> = {
|
||||||
cash: 'تسویه نقدی',
|
cash: 'نقدی',
|
||||||
check: 'تسویه چکی',
|
check: 'چکی',
|
||||||
cash_check: 'تسویه نقدی-چکی',
|
cash_check: 'نصف نقد، نصف چکی',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function settlementTypeLabel(
|
export function settlementTypeLabel(
|
||||||
type: SettlementType | null | undefined,
|
type: SettlementType | null | undefined,
|
||||||
): string {
|
): string {
|
||||||
if (!type) return 'در انتظار انتخاب مشتری'
|
if (!type) return 'هنوز انتخاب نشده'
|
||||||
return SETTLEMENT_TYPE_LABELS[type] ?? type
|
return SETTLEMENT_TYPE_LABELS[type] ?? type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ const {
|
|||||||
showFullDialog,
|
showFullDialog,
|
||||||
submitting,
|
submitting,
|
||||||
submitError,
|
submitError,
|
||||||
|
settlementType,
|
||||||
|
checkTermMonths,
|
||||||
removeItem,
|
removeItem,
|
||||||
proceedToCheckout,
|
proceedToCheckout,
|
||||||
proceedAnyway,
|
proceedAnyway,
|
||||||
@@ -43,6 +45,10 @@ function goToOrdering() {
|
|||||||
:remaining-capacity="cartStore.remainingCapacity"
|
:remaining-capacity="cartStore.remainingCapacity"
|
||||||
:is-empty="isEmpty"
|
:is-empty="isEmpty"
|
||||||
:submitting="submitting"
|
:submitting="submitting"
|
||||||
|
:settlement-type="settlementType"
|
||||||
|
:check-term-months="checkTermMonths"
|
||||||
|
@update:settlement-type="settlementType = $event"
|
||||||
|
@update:check-term-months="checkTermMonths = $event"
|
||||||
@back="router.push('/')"
|
@back="router.push('/')"
|
||||||
@remove="removeItem"
|
@remove="removeItem"
|
||||||
@checkout="proceedToCheckout"
|
@checkout="proceedToCheckout"
|
||||||
@@ -59,6 +65,10 @@ function goToOrdering() {
|
|||||||
:remaining-capacity="cartStore.remainingCapacity"
|
:remaining-capacity="cartStore.remainingCapacity"
|
||||||
:is-empty="isEmpty"
|
:is-empty="isEmpty"
|
||||||
:submitting="submitting"
|
:submitting="submitting"
|
||||||
|
:settlement-type="settlementType"
|
||||||
|
:check-term-months="checkTermMonths"
|
||||||
|
@update:settlement-type="settlementType = $event"
|
||||||
|
@update:check-term-months="checkTermMonths = $event"
|
||||||
@remove="removeItem"
|
@remove="removeItem"
|
||||||
@checkout="proceedToCheckout"
|
@checkout="proceedToCheckout"
|
||||||
@select-company="goToOrdering()"
|
@select-company="goToOrdering()"
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import IconLightbulb from '@/components/icons/IconLightbulb.vue'
|
|||||||
import IconLayoutGrid from '@/components/icons/IconLayoutGrid.vue'
|
import IconLayoutGrid from '@/components/icons/IconLayoutGrid.vue'
|
||||||
import IconDiamond from '@/components/icons/IconDiamond.vue'
|
import IconDiamond from '@/components/icons/IconDiamond.vue'
|
||||||
import { formatMeterage, formatNumber } from '@/utils/format'
|
import { formatMeterage, formatNumber } from '@/utils/format'
|
||||||
import type { CartItem } from '@/types'
|
import type { CartItem, SettlementType } from '@/types'
|
||||||
|
import PreorderPaymentFields from '@/components/orders/PreorderPaymentFields.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
items: CartItem[]
|
items: CartItem[]
|
||||||
@@ -27,6 +28,8 @@ defineProps<{
|
|||||||
remainingCapacity: number
|
remainingCapacity: number
|
||||||
isEmpty: boolean
|
isEmpty: boolean
|
||||||
submitting?: boolean
|
submitting?: boolean
|
||||||
|
settlementType: SettlementType | null
|
||||||
|
checkTermMonths: number | null
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -34,6 +37,8 @@ const emit = defineEmits<{
|
|||||||
checkout: []
|
checkout: []
|
||||||
selectCompany: []
|
selectCompany: []
|
||||||
back: []
|
back: []
|
||||||
|
'update:settlementType': [value: SettlementType]
|
||||||
|
'update:checkTermMonths': [value: number]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -180,6 +185,12 @@ const quickLinks = [
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<PreorderPaymentFields
|
||||||
|
:settlement-type="settlementType"
|
||||||
|
:check-term-months="checkTermMonths"
|
||||||
|
@update:settlement-type="emit('update:settlementType', $event)"
|
||||||
|
@update:check-term-months="emit('update:checkTermMonths', $event)"
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="cart-summary-card__cta"
|
class="cart-summary-card__cta"
|
||||||
@@ -190,7 +201,7 @@ const quickLinks = [
|
|||||||
{{ submitting ? 'در حال ثبت…' : 'ثبت پیشسفارش' }}
|
{{ submitting ? 'در حال ثبت…' : 'ثبت پیشسفارش' }}
|
||||||
</button>
|
</button>
|
||||||
<p class="cart-summary-card__note">
|
<p class="cart-summary-card__note">
|
||||||
پس از ثبت سفارش، مدیریت قیمتگذاری میکند و فاکتور صادر میشود؛ سپس روش تسویه را انتخاب میکنید.
|
روش پرداخت الان ثبت میشود. مدیریت بعد از آن قیمت و متراژ را اعلام میکند.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import EmptyState from '@/components/ui/EmptyState.vue'
|
|||||||
import IconTrash from '@/components/icons/IconTrash.vue'
|
import IconTrash from '@/components/icons/IconTrash.vue'
|
||||||
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
||||||
import { formatMeterage, formatNumber } from '@/utils/format'
|
import { formatMeterage, formatNumber } from '@/utils/format'
|
||||||
import type { CartItem } from '@/types'
|
import type { CartItem, SettlementType } from '@/types'
|
||||||
|
import PreorderPaymentFields from '@/components/orders/PreorderPaymentFields.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
items: CartItem[]
|
items: CartItem[]
|
||||||
@@ -21,6 +22,8 @@ defineProps<{
|
|||||||
remainingCapacity: number
|
remainingCapacity: number
|
||||||
isEmpty: boolean
|
isEmpty: boolean
|
||||||
submitting?: boolean
|
submitting?: boolean
|
||||||
|
settlementType: SettlementType | null
|
||||||
|
checkTermMonths: number | null
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -28,6 +31,8 @@ const emit = defineEmits<{
|
|||||||
remove: [id: string]
|
remove: [id: string]
|
||||||
checkout: []
|
checkout: []
|
||||||
selectCompany: []
|
selectCompany: []
|
||||||
|
'update:settlementType': [value: SettlementType]
|
||||||
|
'update:checkTermMonths': [value: number]
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -98,8 +103,14 @@ const emit = defineEmits<{
|
|||||||
<strong>{{ formatMeterage(remainingCapacity) }}</strong>
|
<strong>{{ formatMeterage(remainingCapacity) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<PreorderPaymentFields
|
||||||
|
:settlement-type="settlementType"
|
||||||
|
:check-term-months="checkTermMonths"
|
||||||
|
@update:settlement-type="emit('update:settlementType', $event)"
|
||||||
|
@update:check-term-months="emit('update:checkTermMonths', $event)"
|
||||||
|
/>
|
||||||
<p class="cart-mobile-page__hint">
|
<p class="cart-mobile-page__hint">
|
||||||
پس از ثبت سفارش، کارشناسان قیمتگذاری میکنند و فاکتور برای شما صادر میشود.
|
پس از ثبت، مدیریت بر اساس همین روش پرداخت قیمت و متراژ را اعلام میکند.
|
||||||
</p>
|
</p>
|
||||||
<BaseButton block :loading="submitting" @click="emit('checkout')">ثبت پیشسفارش</BaseButton>
|
<BaseButton block :loading="submitting" @click="emit('checkout')">ثبت پیشسفارش</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { ref, computed, watch } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useMediaQuery } from '@vueuse/core'
|
import { useMediaQuery } from '@vueuse/core'
|
||||||
import { useOrderStore } from '@/stores/order'
|
import { useOrderStore } from '@/stores/order'
|
||||||
import type { Order } from '@/types'
|
import type { Order, SettlementType } from '@/types'
|
||||||
|
import PreorderPaymentFields from '@/components/orders/PreorderPaymentFields.vue'
|
||||||
import BaseButton from '@/components/ui/BaseButton.vue'
|
import BaseButton from '@/components/ui/BaseButton.vue'
|
||||||
import CurrencyInput from '@/components/ui/CurrencyInput.vue'
|
import CurrencyInput from '@/components/ui/CurrencyInput.vue'
|
||||||
import PersianDateInput from '@/components/ui/PersianDateInput.vue'
|
import PersianDateInput from '@/components/ui/PersianDateInput.vue'
|
||||||
@@ -155,7 +156,37 @@ const trackingFooterLabel = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
function settlementLabel(type: Order['settlementType']) {
|
function settlementLabel(type: Order['settlementType']) {
|
||||||
return settlementTypeLabel(type)
|
const base = settlementTypeLabel(type)
|
||||||
|
const months = order.value?.checkTermMonths
|
||||||
|
if ((type === 'check' || type === 'cash_check') && months) {
|
||||||
|
return `${base} · چک ${months} ماهه`
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
const planType = ref<SettlementType | null>(null)
|
||||||
|
const planMonths = ref<number | null>(null)
|
||||||
|
const planSaving = ref(false)
|
||||||
|
|
||||||
|
async function savePaymentPlan() {
|
||||||
|
const current = order.value
|
||||||
|
if (!current || !planType.value) {
|
||||||
|
formError.value = 'روش پرداخت را انتخاب کنید.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((planType.value === 'check' || planType.value === 'cash_check') && !planMonths.value) {
|
||||||
|
formError.value = 'مدت چک را از ۱ تا ۴ ماه انتخاب کنید.'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
planSaving.value = true
|
||||||
|
formError.value = ''
|
||||||
|
try {
|
||||||
|
await orderStore.setPaymentPlan(current.id, planType.value, planMonths.value)
|
||||||
|
} catch (e) {
|
||||||
|
formError.value = e instanceof Error ? e.message : 'ثبت روش پرداخت ناموفق بود.'
|
||||||
|
} finally {
|
||||||
|
planSaving.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showCashTracking(order: Order) {
|
function showCashTracking(order: Order) {
|
||||||
@@ -314,6 +345,19 @@ async function submitReceipt() {
|
|||||||
<p class="desktop-page__subtitle">{{ order.orderNumber }} · {{ order.companyName }}</p>
|
<p class="desktop-page__subtitle">{{ order.orderNumber }} · {{ order.companyName }}</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="order.status === 'awaiting_review' && !order.settlementType"
|
||||||
|
class="order-info-card desktop-only"
|
||||||
|
>
|
||||||
|
<PreorderPaymentFields
|
||||||
|
:settlement-type="planType"
|
||||||
|
:check-term-months="planMonths"
|
||||||
|
@update:settlement-type="planType = $event"
|
||||||
|
@update:check-term-months="planMonths = $event"
|
||||||
|
/>
|
||||||
|
<BaseButton :loading="planSaving" @click="savePaymentPlan">ثبت روش پرداخت</BaseButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="order-detail-page__body">
|
<div class="order-detail-page__body">
|
||||||
<div v-if="!showTracking" class="order-info-card mobile-only">
|
<div v-if="!showTracking" class="order-info-card mobile-only">
|
||||||
<div class="order-info-card__head">
|
<div class="order-info-card__head">
|
||||||
@@ -340,6 +384,19 @@ async function submitReceipt() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="order.status === 'awaiting_review' && !order.settlementType"
|
||||||
|
class="order-info-card__plan"
|
||||||
|
>
|
||||||
|
<PreorderPaymentFields
|
||||||
|
:settlement-type="planType"
|
||||||
|
:check-term-months="planMonths"
|
||||||
|
@update:settlement-type="planType = $event"
|
||||||
|
@update:check-term-months="planMonths = $event"
|
||||||
|
/>
|
||||||
|
<BaseButton block :loading="planSaving" @click="savePaymentPlan">ثبت روش پرداخت</BaseButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="order-info-card__row order-info-card__row--last">
|
<div class="order-info-card__row order-info-card__row--last">
|
||||||
<span class="order-info-card__key">مبلغ فاکتور</span>
|
<span class="order-info-card__key">مبلغ فاکتور</span>
|
||||||
<strong class="order-info-card__amount">
|
<strong class="order-info-card__amount">
|
||||||
|
|||||||
Reference in New Issue
Block a user