update importWorkflow plugin/fix storeroom pdf print

This commit is contained in:
Gloomy 2025-08-30 13:35:38 +00:00
parent 0fb64e8cfa
commit 3d490ffe51
5 changed files with 202 additions and 131 deletions

View file

@ -1,4 +1,14 @@
<template> <template>
<v-toolbar color="toolbar" :title="'لیست پرونده‌های واردات'">
<template v-slot:prepend>
<v-tooltip :text="$t('dialog.back')" location="bottom">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
icon="mdi-arrow-right" />
</template>
</v-tooltip>
</template>
</v-toolbar>
<div class="import-workflow-list"> <div class="import-workflow-list">
<v-container fluid> <v-container fluid>
<!-- Stats Cards --> <!-- Stats Cards -->
@ -93,72 +103,33 @@
<v-row> <v-row>
<v-col cols="12"> <v-col cols="12">
<v-card> <v-card>
<v-data-table <v-data-table :headers="headers" :items="workflows" :loading="loading" density="comfortable"
:headers="headers" class="elevation-1" :header-props="{ class: 'custom-header' }" hover>
:items="workflows"
:loading="loading"
density="comfortable"
class="elevation-1"
:header-props="{ class: 'custom-header' }"
hover
>
<template v-slot:top> <template v-slot:top>
<!-- موبایل --> <!-- موبایل -->
<div class="d-block d-md-none pa-4"> <div class="d-block d-md-none pa-4">
<div class="d-flex gap-2 flex-column mb-3"> <div class="d-flex gap-2 flex-column mb-3">
<v-btn <v-btn color="primary" prepend-icon="mdi-plus" @click="showCreateDialog = true" size="small" block>
color="primary"
prepend-icon="mdi-plus"
@click="showCreateDialog = true"
size="small"
block
>
پرونده واردات جدید پرونده واردات جدید
</v-btn> </v-btn>
</div> </div>
<v-text-field <v-text-field v-model="filters.search" label="جستجو" prepend-icon="mdi-magnify" clearable
v-model="filters.search" density="compact" variant="outlined" hide-details class="mb-3"
label="جستجو" @update:model-value="loadWorkflows" />
prepend-icon="mdi-magnify"
clearable
density="compact"
variant="outlined"
hide-details
class="mb-3"
@update:model-value="loadWorkflows"
/>
</div> </div>
<!-- دسکتاپ --> <!-- دسکتاپ -->
<div class="d-none d-md-block"> <div class="d-none d-md-block">
<v-toolbar flat style="height: 70px !important; padding: 10px !important;"> <v-toolbar flat style="height: 70px !important; padding: 10px !important;">
<v-text-field <v-text-field v-model="filters.search" label="جستجو" prepend-icon="mdi-magnify" clearable
v-model="filters.search" density="compact" variant="outlined" hide-details style="max-width: 250px;"
label="جستجو" @update:model-value="loadWorkflows" class="ml-2" />
prepend-icon="mdi-magnify"
clearable
density="compact"
variant="outlined"
hide-details
style="max-width: 250px;"
@update:model-value="loadWorkflows"
class="ml-2"
/>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn <v-btn color="info" prepend-icon="mdi-chart-line" @click="goToReports" variant="outlined"
color="info" class="ml-2">
prepend-icon="mdi-chart-line"
@click="goToReports"
variant="outlined"
class="ml-2"
>
گزارشات گزارشات
</v-btn> </v-btn>
<v-btn <v-btn color="primary" prepend-icon="mdi-plus" @click="showCreateDialog = true">
color="primary"
prepend-icon="mdi-plus"
@click="showCreateDialog = true"
>
پرونده واردات جدید پرونده واردات جدید
</v-btn> </v-btn>
</v-toolbar> </v-toolbar>
@ -179,13 +150,7 @@
<template v-slot:item.actions="{ item }"> <template v-slot:item.actions="{ item }">
<v-menu> <v-menu>
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-btn <v-btn v-bind="props" icon="mdi-menu" variant="text" size="small" color="error"></v-btn>
v-bind="props"
icon="mdi-menu"
variant="text"
size="small"
color="error"
></v-btn>
</template> </template>
<v-list> <v-list>
<v-list-item @click="viewWorkflow(item)"> <v-list-item @click="viewWorkflow(item)">
@ -216,10 +181,7 @@
</v-container> </v-container>
<!-- Create Dialog --> <!-- Create Dialog -->
<ImportWorkflowCreateDialog <ImportWorkflowCreateDialog v-model="showCreateDialog" @created="onWorkflowCreated" />
v-model="showCreateDialog"
@created="onWorkflowCreated"
/>
<!-- Delete Confirmation Dialog --> <!-- Delete Confirmation Dialog -->
<v-dialog v-model="showDeleteDialog" max-width="400"> <v-dialog v-model="showDeleteDialog" max-width="400">
@ -237,14 +199,8 @@
</v-dialog> </v-dialog>
<!-- Snackbar for notifications --> <!-- Snackbar for notifications -->
<v-snackbar <v-snackbar v-model="showSnackbar" :color="snackbarColor" :timeout="3000" location="bottom" class="rounded-lg"
v-model="showSnackbar" elevation="2">
:color="snackbarColor"
:timeout="3000"
location="bottom"
class="rounded-lg"
elevation="2"
>
<div class="d-flex align-center"> <div class="d-flex align-center">
<v-icon :color="snackbarColor" class="me-2"> <v-icon :color="snackbarColor" class="me-2">
{{ snackbarColor === 'success' ? 'mdi-check-circle' : 'mdi-alert-circle' }} {{ snackbarColor === 'success' ? 'mdi-check-circle' : 'mdi-alert-circle' }}
@ -334,7 +290,7 @@ const loadWorkflows = async () => {
} }
const response = await axios.get('/api/import-workflow/list', { params }) const response = await axios.get('/api/import-workflow/list', { params })
if (response.data.Success) { if (response.data.Success) {
workflows.value = response.data.Result.data workflows.value = response.data.Result.data
pagination.value.total = response.data.Result.total pagination.value.total = response.data.Result.total
@ -399,7 +355,7 @@ const confirmDelete = async () => {
deleteLoading.value = true deleteLoading.value = true
try { try {
const response = await axios.delete(`/api/import-workflow/${selectedWorkflow.value.id}/delete`) const response = await axios.delete(`/api/import-workflow/${selectedWorkflow.value.id}/delete`)
if (response.data.Success) { if (response.data.Success) {
showNotification('پرونده واردات با موفقیت حذف شد') showNotification('پرونده واردات با موفقیت حذف شد')
loadWorkflows() loadWorkflows()
@ -505,7 +461,7 @@ onMounted(async () => {
} }
.stats-card.active-filter::before { .stats-card.active-filter::before {
background: linear-gradient(45deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.1) 100%); background: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%);
} }
.stats-card::before { .stats-card::before {
@ -515,7 +471,7 @@ onMounted(async () => {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background: linear-gradient(45deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%); background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
border-radius: 16px; border-radius: 16px;
z-index: 1; z-index: 1;
} }
@ -580,5 +536,3 @@ onMounted(async () => {
} }
} }
</style> </style>

View file

@ -1,28 +1,17 @@
<template> <template>
<v-toolbar color="toolbar" :title="'داشبورد گزارشات واردات'">
<template v-slot:prepend>
<v-tooltip :text="$t('dialog.back')" location="bottom">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
icon="mdi-arrow-right" />
</template>
</v-tooltip>
</template>
</v-toolbar>
<div class="import-workflow-dashboard"> <div class="import-workflow-dashboard">
<v-container fluid> <v-container fluid>
<v-row> <v-row style="position: relative; z-index: 999;">
<v-col cols="12">
<v-card class="mb-4">
<v-card-title class="d-flex align-center justify-space-between">
<div class="d-flex align-center">
<v-icon class="ml-2" color="primary">mdi-chart-dashboard</v-icon>
<span class="text-h5">داشبورد گزارشات واردات</span>
</div>
<!-- <div class="d-flex gap-2">
<v-btn color="success" @click="exportToExcel" :loading="exporting" prepend-icon="mdi-file-excel">
خروجی Excel
</v-btn>
<v-btn color="error" @click="exportToPDF" :loading="exporting" prepend-icon="mdi-file-pdf">
خروجی PDF
</v-btn>
</div> -->
</v-card-title>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col cols="12"> <v-col cols="12">
<v-card class="mb-4"> <v-card class="mb-4">
<v-card-title>فیلترهای پیشرفته</v-card-title> <v-card-title>فیلترهای پیشرفته</v-card-title>
@ -48,11 +37,13 @@
<v-row> <v-row>
<v-col cols="12" md="4"> <v-col cols="12" md="4">
<v-text-field v-model="filters.supplierSearch" label="جستجو در تامین‌کنندگان" <v-text-field v-model="filters.supplierSearch" label="جستجو در تامین‌کنندگان"
prepend-icon="mdi-magnify" clearable density="compact" variant="outlined"></v-text-field> prepend-icon="mdi-magnify" clearable density="compact"
variant="outlined"></v-text-field>
</v-col> </v-col>
<v-col cols="12" md="4"> <v-col cols="12" md="4">
<v-text-field v-model="filters.countrySearch" label="جستجو در کشور مبدأ" <v-text-field v-model="filters.countrySearch" label="جستجو در کشور مبدأ"
prepend-icon="mdi-earth" clearable density="compact" variant="outlined"></v-text-field> prepend-icon="mdi-earth" clearable density="compact"
variant="outlined"></v-text-field>
</v-col> </v-col>
<v-col cols="12" md="4"> <v-col cols="12" md="4">
<v-btn color="secondary" @click="clearFilters" variant="outlined" block> <v-btn color="secondary" @click="clearFilters" variant="outlined" block>
@ -239,7 +230,7 @@ const loadWorkflows = async () => {
const calculateSummary = () => { const calculateSummary = () => {
const workflows = filteredWorkflows.value || [] const workflows = filteredWorkflows.value || []
const totalAmount = workflows.reduce((sum, w) => sum + w.totalAmount, 0) const totalAmount = workflows.reduce((sum, w) => sum + w.totalAmount, 0)
const totalPayments = workflows.reduce((sum, w) => sum + w.totalPayments, 0) const totalPayments = workflows.reduce((sum, w) => sum + w.totalPayments, 0)
const totalPaymentsIRR = workflows.reduce((sum, w) => sum + w.totalPaymentsIRR, 0) const totalPaymentsIRR = workflows.reduce((sum, w) => sum + w.totalPaymentsIRR, 0)

View file

@ -478,23 +478,65 @@ const isColumnVisible = (key: string) => {
const LOCAL_STORAGE_KEY = 'hesabix_storeroom_tickets_table_columns'; const LOCAL_STORAGE_KEY = 'hesabix_storeroom_tickets_table_columns';
const printTicket = async (code: string) => { const printTicket = (code: string, pdf = true, cloudePrinters = true) => {
try { loading.value = true;
const response = await axios.post('/api/storeroom/print/ticket', { axios.post('/api/storeroom/print/ticket', {
code: code, 'code': code,
type: 'output' 'type': 'output',
}) 'pdf': pdf,
window.open(`${import.meta.env.VITE_API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer') 'printers': cloudePrinters
} catch (error: any) { }).then(async (response: any) => {
try {
if (response.data && response.data.id) {
const pdfResponse = await axios({
method: 'get',
url: '/front/print/' + response.data.id,
responseType: 'arraybuffer'
});
var fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', `فاکتور انبار ${code}.pdf`);
document.body.appendChild(fileLink);
fileLink.click();
} else {
throw new Error('خطا در دریافت شناسه چاپ');
}
} catch (error) {
console.error('خطا در دریافت فایل PDF:', error);
if (error?.response?.data?.message) {
Swal.fire({
text: error?.response?.data?.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
} else {
Swal.fire({
text: 'خطا در دریافت فایل PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
}
} finally {
loading.value = false;
}
}).catch((error) => {
loading.value = false;
if (error?.response?.data?.message) { if (error?.response?.data?.message) {
Swal.fire({ Swal.fire({
text: error?.response?.data?.message, text: error?.response?.data?.message,
icon: 'warning', icon: 'warning',
confirmButtonText: 'قبول' confirmButtonText: 'قبول'
}); });
} else {
Swal.fire({
text: 'خطا در ایجاد نسخه PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
} }
console.error('Error printing ticket:', error) });
}
} }
const loadColumnSettings = () => { const loadColumnSettings = () => {

View file

@ -244,23 +244,65 @@
const LOCAL_STORAGE_KEY = 'hesabix_storeroom_tickets_table_columns'; const LOCAL_STORAGE_KEY = 'hesabix_storeroom_tickets_table_columns';
const printTicket = async (code: string) => { const printTicket = (code: string, pdf = true, cloudePrinters = true) => {
try { loading.value = true;
const response = await axios.post('/api/storeroom/print/ticket', { axios.post('/api/storeroom/print/ticket', {
code: code, 'code': code,
type: 'output' 'type': 'output',
}) 'pdf': pdf,
window.open(`${import.meta.env.VITE_API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer') 'printers': cloudePrinters
} catch (error: any) { }).then(async (response: any) => {
try {
if (response.data && response.data.id) {
const pdfResponse = await axios({
method: 'get',
url: '/front/print/' + response.data.id,
responseType: 'arraybuffer'
});
var fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', `فاکتور انبار ${code}.pdf`);
document.body.appendChild(fileLink);
fileLink.click();
} else {
throw new Error('خطا در دریافت شناسه چاپ');
}
} catch (error) {
console.error('خطا در دریافت فایل PDF:', error);
if (error?.response?.data?.message) {
Swal.fire({
text: error?.response?.data?.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
} else {
Swal.fire({
text: 'خطا در دریافت فایل PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
}
} finally {
loading.value = false;
}
}).catch((error) => {
loading.value = false;
if (error?.response?.data?.message) { if (error?.response?.data?.message) {
Swal.fire({ Swal.fire({
text: error?.response?.data?.message, text: error?.response?.data?.message,
icon: 'warning', icon: 'warning',
confirmButtonText: 'قبول' confirmButtonText: 'قبول'
}); });
} else {
Swal.fire({
text: 'خطا در ایجاد نسخه PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
} }
console.error('Error printing ticket:', error) });
}
} }
const loadColumnSettings = () => { const loadColumnSettings = () => {

View file

@ -132,23 +132,65 @@ const loadData = async () => {
} }
} }
const printInvoice = async () => { const printInvoice = (pdf = true, cloudePrinters = true) => {
try { loading.value = true;
const response = await axios.post('/api/storeroom/print/ticket', { axios.post('/api/storeroom/print/ticket', {
code: router.currentRoute.value.params.id, 'code': router.currentRoute.value.params.id,
type: item.value.ticket.type 'type': item.value.ticket.type,
}) 'pdf': pdf,
window.open(`${import.meta.env.VITE_API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer') 'printers': cloudePrinters
} catch (error: any) { }).then(async (response: any) => {
try {
if (response.data && response.data.id) {
const pdfResponse = await axios({
method: 'get',
url: '/front/print/' + response.data.id,
responseType: 'arraybuffer'
});
var fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', `فاکتور انبار ${router.currentRoute.value.params.id}.pdf`);
document.body.appendChild(fileLink);
fileLink.click();
} else {
throw new Error('خطا در دریافت شناسه چاپ');
}
} catch (error) {
console.error('خطا در دریافت فایل PDF:', error);
if (error?.response?.data?.message) {
Swal.fire({
text: error?.response?.data?.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
} else {
Swal.fire({
text: 'خطا در دریافت فایل PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
}
} finally {
loading.value = false;
}
}).catch((error) => {
loading.value = false;
if (error?.response?.data?.message) { if (error?.response?.data?.message) {
Swal.fire({ Swal.fire({
text: error?.response?.data?.message, text: error?.response?.data?.message,
icon: 'warning', icon: 'warning',
confirmButtonText: 'قبول' confirmButtonText: 'قبول'
}); });
} else {
Swal.fire({
text: 'خطا در ایجاد نسخه PDF',
icon: 'error',
confirmButtonText: 'قبول'
});
} }
console.error('Error printing invoice:', error) });
}
} }
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {