(null)
+const receiptPreviewOpen = ref(false)
+const receiptPreviewSrc = ref('')
+
+function openReceiptPreview(src: string) {
+ receiptPreviewSrc.value = src
+ receiptPreviewOpen.value = true
+}
const statusFilterOptions = [
{ value: 'all', label: 'همه وضعیتها' },
@@ -144,6 +152,7 @@ async function rejectPayment(payment: CashPayment) {
| شماره پیگیری |
پایان اعتبار |
وضعیت |
+ رسید |
عملیات |
@@ -161,6 +170,17 @@ async function rejectPayment(payment: CashPayment) {
{{ CASH_PAYMENT_STATUS_LABELS[payment.status] }}
+
+
+ —
+ |
+
+
+
diff --git a/src/views/orders/components/OrderAddModal.vue b/src/views/orders/components/OrderAddModal.vue
index 0297e01..571d96d 100644
--- a/src/views/orders/components/OrderAddModal.vue
+++ b/src/views/orders/components/OrderAddModal.vue
@@ -3,10 +3,17 @@ import { computed, reactive, ref, watch } from 'vue'
import BaseModal from '@/components/ui/BaseModal.vue'
import AdminSelect from '@/components/ui/AdminSelect.vue'
import { useOrdersPageContext } from '@/composables/useOrdersPage'
-import { productsApi } from '@/services/api'
+import { productsApi, settingsApi } from '@/services/api'
import { ApiError } from '@/services/http'
import { useToast } from '@/composables/useToast'
import { formatNumber } from '@/utils/format'
+import {
+ meterageFromQuantity,
+ rollMetersForProduct,
+ rollsFromMeterage,
+ validateOrderMeterage,
+ type QuantityMode,
+} from '@/utils/orderItemValidation'
import type { Product } from '@/types'
const {
@@ -24,9 +31,11 @@ const {
const toast = useToast()
const products = ref([])
+const minOrderRolls = ref(1)
const draft = reactive({
productId: '',
- meterage: 0,
+ quantityMode: 'roll' as QuantityMode,
+ quantity: 0,
})
const customerOptions = computed(() =>
@@ -53,6 +62,10 @@ const selectedProduct = computed(() =>
products.value.find((product) => product.id === draft.productId) ?? null,
)
+const quantityLabel = computed(() =>
+ draft.quantityMode === 'roll' ? 'تعداد طاقه' : 'متراژ (متر)',
+)
+
watch(
() => form.customerId,
(id) => {
@@ -65,44 +78,85 @@ watch(
},
)
+function defaultQuantity(product: Product, mode: QuantityMode) {
+ const minRolls = minOrderRolls.value > 0 ? minOrderRolls.value : 1
+ if (mode === 'roll') return minRolls
+ const rollMeters = rollMetersForProduct(product)
+ return Math.max(product.minMeterage || 0, minRolls * rollMeters)
+}
+
watch(showAddModal, async (open) => {
if (!open) return
draft.productId = ''
- draft.meterage = 0
+ draft.quantityMode = 'roll'
+ draft.quantity = 0
+ formError.value = ''
try {
- products.value = await productsApi.list({ activeOnly: true })
+ const [list, settings] = await Promise.all([
+ productsApi.list({ activeOnly: true }),
+ settingsApi.get().catch(() => null),
+ ])
+ products.value = list
+ minOrderRolls.value = settings?.minOrderRollsPerProduct ?? 1
} catch (err) {
toast.error(err instanceof ApiError ? err.message : 'بارگذاری محصولات انجام نشد.')
}
})
watch(
- () => draft.productId,
- (id) => {
+ () => [draft.productId, draft.quantityMode] as const,
+ ([id, mode]) => {
const product = products.value.find((item) => item.id === id)
if (!product) return
- draft.meterage = product.rollMeters || product.minMeterage || product.orderStep || 50
+ draft.quantity = defaultQuantity(product, mode)
},
)
function addDraftProduct() {
+ formError.value = ''
const product = selectedProduct.value
if (!product) {
formError.value = 'محصول را انتخاب کنید.'
+ toast.error(formError.value)
return
}
- if (!draft.meterage || draft.meterage <= 0) {
- formError.value = 'متراژ باید بزرگتر از صفر باشد.'
+ if (!draft.quantity || draft.quantity <= 0) {
+ formError.value = 'مقدار باید بزرگتر از صفر باشد.'
+ toast.error(formError.value)
return
}
- addFormItem({
+
+ const meterage = meterageFromQuantity(product, draft.quantityMode, draft.quantity)
+ const validationError = validateOrderMeterage(product, meterage, minOrderRolls.value)
+ if (validationError) {
+ formError.value = validationError
+ toast.error(validationError)
+ return
+ }
+
+ const added = addFormItem({
productId: product.id,
productName: product.name,
catalogCode: product.catalogCode,
- meterage: draft.meterage,
+ meterage,
})
+ if (!added) {
+ toast.error(formError.value || 'افزودن محصول انجام نشد.')
+ return
+ }
+
draft.productId = ''
- draft.meterage = 0
+ draft.quantityMode = 'roll'
+ draft.quantity = 0
+}
+
+function itemRollsLabel(item: { productId: string; meterage: number }) {
+ const product = products.value.find((p) => p.id === item.productId)
+ if (!product) return null
+ const rolls = rollsFromMeterage(product, item.meterage)
+ if (!Number.isFinite(rolls)) return null
+ const rounded = Math.round(rolls * 100) / 100
+ return rounded % 1 === 0 ? formatNumber(rounded) : formatNumber(rounded)
}
@@ -133,26 +187,49 @@ function addDraftProduct() {
- |