fix some bugs
This commit is contained in:
parent
15d2f40e5d
commit
8d11485530
|
@ -134,6 +134,18 @@ class ApprovalController extends AbstractController
|
||||||
$document->setIsApproved(true);
|
$document->setIsApproved(true);
|
||||||
$document->setApprovedBy($user);
|
$document->setApprovedBy($user);
|
||||||
|
|
||||||
|
$payments = [];
|
||||||
|
foreach ($document->getRelatedDocs() as $relatedDoc) {
|
||||||
|
if ($relatedDoc->getType() === 'sell_receive') {
|
||||||
|
$payments[] = $relatedDoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($payments as $payment) {
|
||||||
|
$payment->setIsPreview(false);
|
||||||
|
$payment->setIsApproved(true);
|
||||||
|
$payment->setApprovedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
$entityManager->persist($document);
|
$entityManager->persist($document);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
|
|
||||||
|
|
|
@ -263,6 +263,16 @@ class BusinessController extends AbstractController
|
||||||
$business->setFinancialApprover($params['financialApprover']);
|
$business->setFinancialApprover($params['financialApprover']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('requireWarrantyOnDelivery', $params)) {
|
||||||
|
$business->setRequireWarrantyOnDelivery($params['requireWarrantyOnDelivery']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('activationGraceDays', $params)) {
|
||||||
|
$business->setActivationGraceDays($params['activationGraceDays']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('matchWarrantyToSerial', $params)) {
|
||||||
|
$business->setMatchWarrantyToSerial($params['matchWarrantyToSerial']);
|
||||||
|
}
|
||||||
|
|
||||||
//get Money type
|
//get Money type
|
||||||
if (!array_key_exists('arzmain', $params) && $isNew) {
|
if (!array_key_exists('arzmain', $params) && $isNew) {
|
||||||
return $this->json(['result' => 2]);
|
return $this->json(['result' => 2]);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Business;
|
||||||
use App\Service\AccountingPermissionService;
|
use App\Service\AccountingPermissionService;
|
||||||
use App\Service\Jdate;
|
use App\Service\Jdate;
|
||||||
use App\Service\Log;
|
use App\Service\Log;
|
||||||
|
@ -73,7 +74,7 @@ class SellController extends AbstractController
|
||||||
{
|
{
|
||||||
$acc = $access->hasRole('sell');
|
$acc = $access->hasRole('sell');
|
||||||
if (!$acc) throw $this->createAccessDeniedException();
|
if (!$acc) throw $this->createAccessDeniedException();
|
||||||
$doc = $entityManager->getRepository(\App\Entity\HesabdariDoc::class)->findOneBy([
|
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||||
'bid' => $acc['bid'],
|
'bid' => $acc['bid'],
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'money' => $acc['money']
|
'money' => $acc['money']
|
||||||
|
@ -90,7 +91,7 @@ class SellController extends AbstractController
|
||||||
{
|
{
|
||||||
$acc = $access->hasRole('sell');
|
$acc = $access->hasRole('sell');
|
||||||
if (!$acc) throw $this->createAccessDeniedException();
|
if (!$acc) throw $this->createAccessDeniedException();
|
||||||
$paymentDoc = $entityManager->getRepository(\App\Entity\HesabdariDoc::class)->findOneBy([
|
$paymentDoc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
|
||||||
'bid' => $acc['bid'],
|
'bid' => $acc['bid'],
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'money' => $acc['money'],
|
'money' => $acc['money'],
|
||||||
|
@ -333,7 +334,7 @@ class SellController extends AbstractController
|
||||||
$entityManager->persist($hesabdariRow);
|
$entityManager->persist($hesabdariRow);
|
||||||
|
|
||||||
// Two-step approval: اگر کسبوکار تأیید دو مرحلهای را الزامی کرده باشد
|
// Two-step approval: اگر کسبوکار تأیید دو مرحلهای را الزامی کرده باشد
|
||||||
$business = $entityManager->getRepository(\App\Entity\Business::class)->find($acc['bid']);
|
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
|
||||||
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
||||||
if ($businessRequire) {
|
if ($businessRequire) {
|
||||||
$doc->setIsPreview(true);
|
$doc->setIsPreview(true);
|
||||||
|
@ -847,10 +848,13 @@ class SellController extends AbstractController
|
||||||
$accountStatus['label'] = 'بدهکار';
|
$accountStatus['label'] = 'بدهکار';
|
||||||
$accountStatus['value'] = $bd - $bs;
|
$accountStatus['value'] = $bd - $bs;
|
||||||
}
|
}
|
||||||
// فقط در صورت تایید نهایی مجاز به چاپ هستیم
|
|
||||||
if ($doc->getStatus() !== 'approved') {
|
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
|
||||||
|
$twoApproval = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
||||||
|
if ($twoApproval && $doc->isApproved() !== true && $doc->isPreview() == true) {
|
||||||
return $this->json(['result' => -10, 'message' => 'فاکتور هنوز تایید نشده است'], 403);
|
return $this->json(['result' => -10, 'message' => 'فاکتور هنوز تایید نشده است'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($params['pdf'] == true || $params['printers'] == true) {
|
if ($params['pdf'] == true || $params['printers'] == true) {
|
||||||
$note = '';
|
$note = '';
|
||||||
if ($printSettings) {
|
if ($printSettings) {
|
||||||
|
@ -1261,7 +1265,7 @@ class SellController extends AbstractController
|
||||||
$entityManager->persist($hesabdariRow);
|
$entityManager->persist($hesabdariRow);
|
||||||
|
|
||||||
// Two-step approval: اگر کسبوکار تأیید دو مرحلهای را الزامی کرده باشد
|
// Two-step approval: اگر کسبوکار تأیید دو مرحلهای را الزامی کرده باشد
|
||||||
$business = $entityManager->getRepository(\App\Entity\Business::class)->find($acc['bid']);
|
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
|
||||||
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
||||||
if ($businessRequire) {
|
if ($businessRequire) {
|
||||||
$doc->setIsPreview(true);
|
$doc->setIsPreview(true);
|
||||||
|
@ -1299,6 +1303,8 @@ class SellController extends AbstractController
|
||||||
$paymentDoc->setDate($params['invoiceDate']);
|
$paymentDoc->setDate($params['invoiceDate']);
|
||||||
$paymentDoc->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
|
$paymentDoc->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
|
||||||
$paymentDoc->setAmount($payment['amount']);
|
$paymentDoc->setAmount($payment['amount']);
|
||||||
|
$paymentDoc->setIsPreview(true);
|
||||||
|
$paymentDoc->setIsApproved(false);
|
||||||
|
|
||||||
// ایجاد ارتباط با فاکتور اصلی
|
// ایجاد ارتباط با فاکتور اصلی
|
||||||
$doc->addRelatedDoc($paymentDoc);
|
$doc->addRelatedDoc($paymentDoc);
|
||||||
|
|
|
@ -238,6 +238,9 @@ class StoreroomController extends AbstractController
|
||||||
]);
|
]);
|
||||||
$sellsForExport = [];
|
$sellsForExport = [];
|
||||||
foreach ($sells as $sell) {
|
foreach ($sells as $sell) {
|
||||||
|
if ($sell->isPreview()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$temp = $provider->Entity2Array($sell, 0);
|
$temp = $provider->Entity2Array($sell, 0);
|
||||||
$person = $this->getPerson($sell);
|
$person = $this->getPerson($sell);
|
||||||
if ($person) {
|
if ($person) {
|
||||||
|
@ -263,6 +266,9 @@ class StoreroomController extends AbstractController
|
||||||
]);
|
]);
|
||||||
$rfsellsForExport = [];
|
$rfsellsForExport = [];
|
||||||
foreach ($rfsells as $sell) {
|
foreach ($rfsells as $sell) {
|
||||||
|
if ($sell->isPreview()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$temp = $provider->Entity2Array($sell, 0);
|
$temp = $provider->Entity2Array($sell, 0);
|
||||||
$person = $this->getPerson($sell);
|
$person = $this->getPerson($sell);
|
||||||
if ($person) {
|
if ($person) {
|
||||||
|
@ -288,6 +294,9 @@ class StoreroomController extends AbstractController
|
||||||
]);
|
]);
|
||||||
$rfbuysForExport = [];
|
$rfbuysForExport = [];
|
||||||
foreach ($rfbuys as $buy) {
|
foreach ($rfbuys as $buy) {
|
||||||
|
if ($buy->isPreview()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$temp = $provider->Entity2Array($buy, 0);
|
$temp = $provider->Entity2Array($buy, 0);
|
||||||
$person = $this->getPerson($buy);
|
$person = $this->getPerson($buy);
|
||||||
if ($person) {
|
if ($person) {
|
||||||
|
@ -910,12 +919,12 @@ class StoreroomController extends AbstractController
|
||||||
|
|
||||||
$business = $entityManager->getRepository(\App\Entity\Business::class)->find($acc['bid']);
|
$business = $entityManager->getRepository(\App\Entity\Business::class)->find($acc['bid']);
|
||||||
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
|
||||||
if ($businessRequire) {
|
if ($businessRequire && $doc->isApproved() !== true && $doc->isPreview() == true) {
|
||||||
// بررسی وضعیت تأیید از طریق StoreroomTicket
|
|
||||||
if ($doc->isPreview()) {
|
if ($doc->isPreview()) {
|
||||||
return $this->json(['result' => -10, 'message' => 'حواله هنوز تایید نشده است'], 403);
|
return $this->json(['result' => -10, 'message' => 'حواله هنوز تایید نشده است'], 403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pdfPid = 0;
|
$pdfPid = 0;
|
||||||
$pdfPid = $provider->createPrint(
|
$pdfPid = $provider->createPrint(
|
||||||
$acc['bid'],
|
$acc['bid'],
|
||||||
|
|
|
@ -584,6 +584,9 @@ class Explore
|
||||||
'financialApprover' => $item->getFinancialApprover(),
|
'financialApprover' => $item->getFinancialApprover(),
|
||||||
'updateSellPrice' => $item->isCommodityUpdateSellPriceAuto(),
|
'updateSellPrice' => $item->isCommodityUpdateSellPriceAuto(),
|
||||||
'updateBuyPrice' => $item->isCommodityUpdateBuyPriceAuto(),
|
'updateBuyPrice' => $item->isCommodityUpdateBuyPriceAuto(),
|
||||||
|
'requireWarrantyOnDelivery' => $item->getRequireWarrantyOnDelivery(),
|
||||||
|
'activationGraceDays' => $item->getActivationGraceDays(),
|
||||||
|
'matchWarrantyToSerial' => $item->getMatchWarrantyToSerial(),
|
||||||
];
|
];
|
||||||
if (!$item->getProfitCalctype()) {
|
if (!$item->getProfitCalctype()) {
|
||||||
$res['profitCalcType'] = 'lis';
|
$res['profitCalcType'] = 'lis';
|
||||||
|
|
|
@ -507,6 +507,7 @@ const fa_lang = {
|
||||||
basic_info: "اطلاعات پایه",
|
basic_info: "اطلاعات پایه",
|
||||||
year_label: "سال مالی جاری",
|
year_label: "سال مالی جاری",
|
||||||
global_settings: "تنظیمات سراسری",
|
global_settings: "تنظیمات سراسری",
|
||||||
|
warranty_settings: "تنظیمات گارانتی",
|
||||||
gate_pay: "درگاه پرداخت",
|
gate_pay: "درگاه پرداخت",
|
||||||
a4l: "کاغذ A4 افقی",
|
a4l: "کاغذ A4 افقی",
|
||||||
a4p: "کاغذ A4 عمودی",
|
a4p: "کاغذ A4 عمودی",
|
||||||
|
|
|
@ -892,6 +892,12 @@ const router = createRouter({
|
||||||
'login': true
|
'login': true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'plugins/warranty/intro',
|
||||||
|
name: 'plugin_warranty_intro',
|
||||||
|
component: () =>
|
||||||
|
import('../views/acc/plugins/warranty/intro.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'notifications/list',
|
path: 'notifications/list',
|
||||||
name: 'notification_list',
|
name: 'notification_list',
|
||||||
|
@ -1080,6 +1086,12 @@ const router = createRouter({
|
||||||
'login': true,
|
'login': true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'plugins/import-workflow/intro',
|
||||||
|
name: 'import_workflow_intro',
|
||||||
|
component: () =>
|
||||||
|
import('../views/acc/plugins/import-workflow/intro.vue'),
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
195
webUI/src/views/acc/plugins/import-workflow/intro.vue
Normal file
195
webUI/src/views/acc/plugins/import-workflow/intro.vue
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
<template>
|
||||||
|
<div class="plugin-intro">
|
||||||
|
<div class="intro-header">
|
||||||
|
<div class="plugin-icon">
|
||||||
|
<i class="fas fa-ship"></i>
|
||||||
|
</div>
|
||||||
|
<div class="plugin-info">
|
||||||
|
<h1>افزونه مدیریت واردات کالا</h1>
|
||||||
|
<p class="plugin-description">
|
||||||
|
مدیریت پروندههای واردات از ثبت تا ترخیص: اسناد، اقلام، پرداختها، حمل و مراحل
|
||||||
|
</p>
|
||||||
|
<div class="plugin-version">
|
||||||
|
<span class="version-badge">نسخه 1.0.0</span>
|
||||||
|
<span v-if="isPluginActive('importWorkflow')" class="status-badge active">فعال</span>
|
||||||
|
<RouterLink to="/acc/plugin-center/view-end/import-workflow" v-if="!isPluginActive('importWorkflow')">
|
||||||
|
<span class="status-badge active text-white d-flex align-items-center">
|
||||||
|
<i class="fa fa-shopping-cart me-1"></i>
|
||||||
|
خرید
|
||||||
|
</span>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="intro-content">
|
||||||
|
<div class="features-section">
|
||||||
|
<h2>امکانات افزونه</h2>
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-folder-plus"></i>
|
||||||
|
</div>
|
||||||
|
<h3>پرونده واردات</h3>
|
||||||
|
<p>ایجاد و مدیریت پروندههای واردات با کد یکتا و وضعیت</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-file-alt"></i>
|
||||||
|
</div>
|
||||||
|
<h3>مدیریت اسناد</h3>
|
||||||
|
<p>بارگذاری و سازماندهی اسناد (پروفرما، پکینگ، حواله، ترخیص و ...)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-boxes"></i>
|
||||||
|
</div>
|
||||||
|
<h3>اقلام و جزئیات</h3>
|
||||||
|
<p>ثبت اقلام، ارزش، وزن، حجم، برند، مدل و مشخصات کالا</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-money-bill-wave"></i>
|
||||||
|
</div>
|
||||||
|
<h3>پرداختها و هزینهها</h3>
|
||||||
|
<p>مدیریت پرداختها، ارز، نرخ تبدیل و هزینههای مرتبط</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-truck-loading"></i>
|
||||||
|
</div>
|
||||||
|
<h3>حمل و لجستیک</h3>
|
||||||
|
<p>ثبت اطلاعات حمل، کانتینر، مسیر و تاریخها</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<i class="fas fa-stream"></i>
|
||||||
|
</div>
|
||||||
|
<h3>مراحل و پیگیری</h3>
|
||||||
|
<p>تعریف مراحل، وضعیتها، یادداشتها و پیگیری پیشرفت</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="validation-section">
|
||||||
|
<h2>کنترلها</h2>
|
||||||
|
<div class="validation-list">
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>کنترل کامل اطلاعات موردنیاز ترخیص</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>همگامسازی اقلام با موجودی و کد کالا</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>گزارشگیری از هزینهها و وضعیت پرونده</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setup-section">
|
||||||
|
<h2>مراحل راهاندازی</h2>
|
||||||
|
<div class="setup-steps">
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>فعالسازی افزونه</h4>
|
||||||
|
<p>افزونه را از مرکز افزونهها فعال کنید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>ایجاد پرونده</h4>
|
||||||
|
<p>پرونده جدید واردات ایجاد و مشخصات پایه را ثبت کنید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>ثبت اسناد و اقلام</h4>
|
||||||
|
<p>اسناد، اقلام، پرداختها و حمل را به پرونده اضافه کنید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="intro-footer">
|
||||||
|
<div v-if="isPluginActive('importWorkflow')" class="action-buttons">
|
||||||
|
<router-link to="/acc/plugins/import-workflow/list" class="btn btn-success">
|
||||||
|
<i class="fas fa-list"></i>
|
||||||
|
ورود به مدیریت واردات
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 20px; font-size: 0.75rem; color: #888; text-align: center;">
|
||||||
|
Developed by <a href="https://pirouz.xyz" target="_blank" style="color: #667eea; text-decoration: none;">Mohammad Rezai</a> – 2025
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ImportWorkflowIntro',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
plugins: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isPluginActive(plugName) {
|
||||||
|
return this.plugins && this.plugins[plugName] !== undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.post('/api/plugin/get/actives').then((response) => {
|
||||||
|
this.plugins = response.data;
|
||||||
|
});
|
||||||
|
if (this.$store) {
|
||||||
|
this.$store.commit('setPageTitle', 'مدیریت واردات کالا - معرفی افزونه')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* reuse tax intro styles for consistent look */
|
||||||
|
.plugin-intro { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
||||||
|
.intro-header { display: flex; align-items: center; gap: 20px; margin-bottom: 40px; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white; }
|
||||||
|
.plugin-icon { font-size: 3rem; background: rgba(255, 255, 255, 0.2); width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
.plugin-info h1 { margin: 0 0 10px 0; font-size: 2.5rem; font-weight: 700; }
|
||||||
|
.plugin-description { font-size: 1.1rem; margin: 0 0 15px 0; opacity: 0.9; }
|
||||||
|
.plugin-version { display: flex; gap: 10px; }
|
||||||
|
.version-badge, .status-badge { padding: 5px 12px; border-radius: 20px; font-size: 0.9rem; font-weight: 600; }
|
||||||
|
.version-badge { background: rgba(255, 255, 255, 0.2); }
|
||||||
|
.status-badge.active { background: #28a745; }
|
||||||
|
.intro-content { display: flex; flex-direction: column; gap: 40px; }
|
||||||
|
.features-section, .invoice-types-section, .validation-section, .setup-section, .compliance-section { background: white; border-radius: 15px; padding: 30px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }
|
||||||
|
.features-section h2, .invoice-types-section h2, .validation-section h2, .setup-section h2, .compliance-section h2 { margin: 0 0 25px 0; font-size: 1.8rem; color: #333; border-bottom: 3px solid #667eea; padding-bottom: 10px; }
|
||||||
|
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 25px; }
|
||||||
|
.feature-card { padding: 25px; border-radius: 12px; background: #f8f9fa; border-left: 4px solid #667eea; transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
||||||
|
.feature-card:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); }
|
||||||
|
.feature-icon { font-size: 2rem; color: #667eea; margin-bottom: 15px; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; background: rgba(102, 126, 234, 0.1); border-radius: 50%; }
|
||||||
|
.validation-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; }
|
||||||
|
.validation-item { display: flex; align-items: center; gap: 12px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #28a745; }
|
||||||
|
.setup-steps { display: flex; flex-direction: column; gap: 20px; }
|
||||||
|
.setup-step { display: flex; align-items: flex-start; gap: 20px; padding: 20px; background: #f8f9fa; border-radius: 12px; border-left: 4px solid #667eea; }
|
||||||
|
.step-number { background: #667eea; color: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.2rem; flex-shrink: 0; }
|
||||||
|
.action-buttons { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; }
|
||||||
|
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; border: none; cursor: pointer; }
|
||||||
|
.btn-success { background: #28a745; color: white; }
|
||||||
|
.btn-success:hover { background: #218838; transform: translateY(-2px); }
|
||||||
|
@media (max-width: 768px) { .plugin-intro { padding: 15px; } .intro-header { flex-direction: column; text-align: center; padding: 20px; gap: 15px; } .plugin-icon { width: 60px; height: 60px; font-size: 2rem; } .plugin-info h1 { font-size: 1.8rem; } .features-section, .invoice-types-section, .validation-section, .setup-section, .compliance-section { padding: 20px; } .features-grid { grid-template-columns: 1fr; gap: 20px; } .feature-card { padding: 20px; } .feature-icon { width: 50px; height: 50px; font-size: 1.5rem; } .action-buttons { flex-direction: column; gap: 15px; } .btn { justify-content: center; padding: 15px 20px; font-size: 1rem; } }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
|
@ -127,141 +127,58 @@
|
||||||
<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="serials" :loading="loading" density="comfortable"
|
||||||
:headers="headers"
|
class="elevation-1" :header-props="{ class: 'custom-header' }" hover show-select v-model="selectedItems"
|
||||||
:items="serials"
|
item-key="id" return-object :items-per-page="50">
|
||||||
:loading="loading"
|
|
||||||
density="comfortable"
|
|
||||||
class="elevation-1"
|
|
||||||
:header-props="{ class: 'custom-header' }"
|
|
||||||
hover
|
|
||||||
show-select
|
|
||||||
v-model="selectedItems"
|
|
||||||
item-key="id"
|
|
||||||
return-object
|
|
||||||
:items-per-page="50"
|
|
||||||
>
|
|
||||||
<template #top>
|
<template #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 v-if="selectedItems.length > 0" color="error" prepend-icon="mdi-delete-multiple"
|
||||||
v-if="selectedItems.length > 0"
|
@click="showDeleteBulkDialog = true" size="small" block>
|
||||||
color="error"
|
حذف انتخاب شدهها <v-chip size="small" color="error" variant="tonal" class="ms-2"
|
||||||
prepend-icon="mdi-delete-multiple"
|
style="color: white !important;">{{ selectedItems.length }}</v-chip>
|
||||||
@click="showDeleteBulkDialog = true"
|
|
||||||
size="small"
|
|
||||||
block
|
|
||||||
>
|
|
||||||
حذف انتخاب شدهها <v-chip size="small" color="error" variant="tonal" class="ms-2" style="color: white !important;">{{ selectedItems.length }}</v-chip>
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="addSerial" size="small" block>
|
<v-btn color="primary" prepend-icon="mdi-plus" @click="addSerial" size="small" block>
|
||||||
افزودن سریال
|
افزودن سریال
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="secondary" prepend-icon="mdi-upload" @click="showBulkImportDialog = true" size="small" block>
|
<v-btn color="secondary" prepend-icon="mdi-upload" @click="showBulkImportDialog = 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" @update:model-value="loadSerials" />
|
||||||
label="جستجو"
|
|
||||||
prepend-icon="mdi-magnify"
|
|
||||||
clearable
|
|
||||||
density="compact"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
class="mb-3"
|
|
||||||
@update:model-value="loadSerials"
|
|
||||||
/>
|
|
||||||
<div class="d-flex gap-2 mb-3">
|
<div class="d-flex gap-2 mb-3">
|
||||||
<v-select
|
<v-select v-model="filters.status" label="وضعیت" :items="statusOptions" clearable density="compact"
|
||||||
v-model="filters.status"
|
variant="outlined" hide-details style="flex: 1;" @update:model-value="loadSerials" />
|
||||||
label="وضعیت"
|
<v-select v-model="filters.commodity_id" label="محصول" :items="commodities" item-title="name"
|
||||||
:items="statusOptions"
|
item-value="id" clearable density="compact" variant="outlined" hide-details style="flex: 1;"
|
||||||
clearable
|
@update:model-value="loadSerials" />
|
||||||
density="compact"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
style="flex: 1;"
|
|
||||||
@update:model-value="loadSerials"
|
|
||||||
/>
|
|
||||||
<v-select
|
|
||||||
v-model="filters.commodity_id"
|
|
||||||
label="محصول"
|
|
||||||
:items="commodities"
|
|
||||||
item-title="name"
|
|
||||||
item-value="id"
|
|
||||||
clearable
|
|
||||||
density="compact"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
style="flex: 1;"
|
|
||||||
@update:model-value="loadSerials"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</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="loadSerials" class="ml-2" />
|
||||||
prepend-icon="mdi-magnify"
|
<v-select v-model="filters.status" label="وضعیت" :items="statusOptions" clearable density="compact"
|
||||||
clearable
|
variant="outlined" hide-details style="max-width: 220px;" @update:model-value="loadSerials"
|
||||||
density="compact"
|
class="ml-2" />
|
||||||
variant="outlined"
|
<v-select v-model="filters.commodity_id" label="محصول" :items="commodities" item-title="name"
|
||||||
hide-details
|
item-value="id" clearable density="compact" variant="outlined" hide-details
|
||||||
style="max-width: 250px;"
|
style="max-width: 250px;" @update:model-value="loadSerials" class="ml-2" />
|
||||||
@update:model-value="loadSerials"
|
|
||||||
class="ml-2"
|
|
||||||
/>
|
|
||||||
<v-select
|
|
||||||
v-model="filters.status"
|
|
||||||
label="وضعیت"
|
|
||||||
:items="statusOptions"
|
|
||||||
clearable
|
|
||||||
density="compact"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
style="max-width: 220px;"
|
|
||||||
@update:model-value="loadSerials"
|
|
||||||
class="ml-2"
|
|
||||||
/>
|
|
||||||
<v-select
|
|
||||||
v-model="filters.commodity_id"
|
|
||||||
label="محصول"
|
|
||||||
:items="commodities"
|
|
||||||
item-title="name"
|
|
||||||
item-value="id"
|
|
||||||
clearable
|
|
||||||
density="compact"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
style="max-width: 250px;"
|
|
||||||
@update:model-value="loadSerials"
|
|
||||||
class="ml-2"
|
|
||||||
/>
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn
|
<v-btn v-if="selectedItems.length > 0" color="error" prepend-icon="mdi-delete-multiple"
|
||||||
variant="tonal"
|
@click="showDeleteBulkDialog = true" class="ml-2">
|
||||||
prepend-icon="mdi-cog"
|
حذف انتخاب شدهها <v-chip size="small" color="error" variant="tonal" class="ms-2"
|
||||||
class="ml-2"
|
style="color: red !important;">{{ selectedItems.length }}</v-chip>
|
||||||
@click="openSettings"
|
|
||||||
>
|
|
||||||
تنظیمات
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-if="selectedItems.length > 0"
|
|
||||||
color="error"
|
|
||||||
prepend-icon="mdi-delete-multiple"
|
|
||||||
@click="showDeleteBulkDialog = true"
|
|
||||||
class="ml-2"
|
|
||||||
>
|
|
||||||
حذف انتخاب شدهها <v-chip size="small" color="error" variant="tonal" class="ms-2" style="color: red !important;">{{ selectedItems.length }}</v-chip>
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="addSerial">
|
<v-btn color="primary" prepend-icon="mdi-plus" @click="addSerial">
|
||||||
افزودن سریال
|
افزودن سریال
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="secondary" prepend-icon="mdi-upload" @click="showBulkImportDialog = true" class="ml-2">
|
<v-btn color="secondary" prepend-icon="mdi-upload" @click="showBulkImportDialog = true"
|
||||||
|
class="ml-2">
|
||||||
وارد کردن انبوه
|
وارد کردن انبوه
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
@ -293,20 +210,15 @@
|
||||||
<span>
|
<span>
|
||||||
{{ (item as any).warrantyEndDate ? formatDate((item as any).warrantyEndDate) : '-' }}
|
{{ (item as any).warrantyEndDate ? formatDate((item as any).warrantyEndDate) : '-' }}
|
||||||
</span>
|
</span>
|
||||||
<v-chip
|
<v-chip v-if="(item as any).expired" color="error" size="x-small" variant="flat" class="ms-2">
|
||||||
v-if="(item as any).expired"
|
|
||||||
color="error"
|
|
||||||
size="x-small"
|
|
||||||
variant="flat"
|
|
||||||
class="ms-2"
|
|
||||||
>
|
|
||||||
منقضی
|
منقضی
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item.commodity="{ item }">
|
<template #item.commodity="{ item }">
|
||||||
<v-btn variant="text" color="primary" size="small" @click="viewCommodity((item as any).commodity)" class="text-none">
|
<v-btn variant="text" color="primary" size="small" @click="viewCommodity((item as any).commodity)"
|
||||||
|
class="text-none">
|
||||||
{{ (item as any).commodity?.name || 'نامشخص' }}
|
{{ (item as any).commodity?.name || 'نامشخص' }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
|
@ -338,26 +250,13 @@
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
||||||
<SerialDialog
|
<SerialDialog v-model="showAddDialog" :serial="selectedSerial" :commodities="commodities" @save="saveSerial"
|
||||||
v-model="showAddDialog"
|
@close="closeDialog" />
|
||||||
:serial="selectedSerial"
|
|
||||||
:commodities="commodities"
|
|
||||||
@save="saveSerial"
|
|
||||||
@close="closeDialog"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SerialViewDialog
|
<SerialViewDialog v-model="showViewDialog" :serial="selectedSerial" @close="closeViewDialog" />
|
||||||
v-model="showViewDialog"
|
|
||||||
:serial="selectedSerial"
|
|
||||||
@close="closeViewDialog"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<BulkImportDialog
|
<BulkImportDialog v-model="showBulkImportDialog" :commodities="commodities" @import="bulkImport"
|
||||||
v-model="showBulkImportDialog"
|
@close="closeBulkImportDialog" />
|
||||||
:commodities="commodities"
|
|
||||||
@import="bulkImport"
|
|
||||||
@close="closeBulkImportDialog"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-dialog v-model="showDeleteDialog" max-width="400">
|
<v-dialog v-model="showDeleteDialog" max-width="400">
|
||||||
<v-card>
|
<v-card>
|
||||||
|
@ -387,55 +286,10 @@
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
<v-dialog v-model="showSettingsDialog" max-width="500">
|
<!-- تنظیمات گارانتی به تب تنظیمات کسبوکار منتقل شد -->
|
||||||
<v-card :loading="settingsLoading || loading">
|
|
||||||
<v-toolbar color="primary" flat>
|
|
||||||
<v-icon color="white" class="ms-2">mdi-cog</v-icon>
|
|
||||||
<v-toolbar-title class="text-white text-subtitle-1">تنظیمات گارانتی</v-toolbar-title>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn icon @click="showSettingsDialog = false">
|
|
||||||
<v-icon color="white">mdi-close</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-toolbar>
|
|
||||||
<v-card-text>
|
|
||||||
<v-switch
|
|
||||||
v-model="settings.requireWarrantyOnDelivery"
|
|
||||||
label="الزام ثبت گارانتی هنگام صدور حواله"
|
|
||||||
color="primary"
|
|
||||||
inset
|
|
||||||
hide-details
|
|
||||||
:disabled="settingsLoading || loading"
|
|
||||||
/>
|
|
||||||
<v-switch
|
|
||||||
v-model="settings.matchWarrantyToSerial"
|
|
||||||
label="تطبیق خودکار گارانتی با سریال کالا (در صورت یکسان بودن کد)"
|
|
||||||
color="primary"
|
|
||||||
inset
|
|
||||||
hide-details
|
|
||||||
class="mt-2"
|
|
||||||
:disabled="settingsLoading || loading"
|
|
||||||
/>
|
|
||||||
<v-text-field
|
|
||||||
v-model.number="settings.activationGraceDays"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
label="مهلت فعالسازی گارانتی (روز)"
|
|
||||||
variant="outlined"
|
|
||||||
density="compact"
|
|
||||||
hide-details
|
|
||||||
class="mt-4"
|
|
||||||
:disabled="settingsLoading || loading"
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer />
|
|
||||||
<v-btn color="grey" variant="text" @click="showSettingsDialog = false">انصراف</v-btn>
|
|
||||||
<v-btn color="primary" :loading="loading" :disabled="settingsLoading" @click="saveSettings">ذخیره</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-snackbar v-model="showSnackbar" :color="snackbarColor" :timeout="3000" location="bottom" class="rounded-lg" elevation="2">
|
<v-snackbar v-model="showSnackbar" :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' }}
|
||||||
|
@ -880,27 +734,34 @@ onMounted(async () => {
|
||||||
.warranty-plugin {
|
.warranty-plugin {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-data-table {
|
.v-data-table {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.v-data-table-header th) {
|
:deep(.v-data-table-header th) {
|
||||||
background-color: #f5f5f5 !important;
|
background-color: #f5f5f5 !important;
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
color: #333 !important;
|
color: #333 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.v-data-table__wrapper table td) {
|
:deep(.v-data-table__wrapper table td) {
|
||||||
padding: 12px 16px !important;
|
padding: 12px 16px !important;
|
||||||
border-bottom: 1px solid #e0e0e0 !important;
|
border-bottom: 1px solid #e0e0e0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.v-data-table__wrapper table tr:hover) {
|
:deep(.v-data-table__wrapper table tr:hover) {
|
||||||
background-color: #f8f9fa !important;
|
background-color: #f8f9fa !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.v-chip) {
|
:deep(.v-chip) {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-header {
|
.custom-header {
|
||||||
background-color: #f5f5f5 !important;
|
background-color: #f5f5f5 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-card {
|
.stats-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
@ -916,18 +777,22 @@ onMounted(async () => {
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-card:focus {
|
.stats-card:focus {
|
||||||
outline: 2px solid rgba(255, 255, 255, 0.5);
|
outline: 2px solid rgba(255, 255, 255, 0.5);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-card.active-filter {
|
.stats-card.active-filter {
|
||||||
transform: translateY(-4px) scale(1.02);
|
transform: translateY(-4px) scale(1.02);
|
||||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
|
||||||
border: 2px solid rgba(255, 255, 255, 0.8);
|
border: 2px solid rgba(255, 255, 255, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.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 {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -935,14 +800,16 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-card:hover {
|
.stats-card:hover {
|
||||||
transform: translateY(-8px) scale(1.01);
|
transform: translateY(-8px) scale(1.01);
|
||||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-icon {
|
.stats-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -951,11 +818,13 @@ onMounted(async () => {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-content {
|
.stats-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-number {
|
.stats-number {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -963,24 +832,30 @@ onMounted(async () => {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-label {
|
.stats-label {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-card {
|
.total-card {
|
||||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-card {
|
.active-card {
|
||||||
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.expired-card {
|
.expired-card {
|
||||||
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inactive-card {
|
.inactive-card {
|
||||||
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
|
background: linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.stats-card {
|
.stats-card {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
224
webUI/src/views/acc/plugins/warranty/intro.vue
Normal file
224
webUI/src/views/acc/plugins/warranty/intro.vue
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
<template>
|
||||||
|
<div class="plugin-intro">
|
||||||
|
<div class="intro-header">
|
||||||
|
<div class="plugin-icon">
|
||||||
|
<i class="fas fa-shield-alt"></i>
|
||||||
|
</div>
|
||||||
|
<div class="plugin-info">
|
||||||
|
<h1>افزونه مدیریت گارانتی</h1>
|
||||||
|
<p class="plugin-description">
|
||||||
|
مدیریت چرخهعمر گارانتی از ثبت سریال تا تخصیص، اسکن، ارسال برای مشتری و فعالسازی آنلاین
|
||||||
|
</p>
|
||||||
|
<div class="plugin-version">
|
||||||
|
<span class="version-badge">نسخه 1.0.0</span>
|
||||||
|
<span v-if="isPluginActive('warranty')" class="status-badge active">فعال</span>
|
||||||
|
<RouterLink to="/acc/plugin-center/view-end/warranty" v-if="!isPluginActive('warranty')">
|
||||||
|
<span class="status-badge active text-white d-flex align-items-center">
|
||||||
|
<i class="fa fa-shopping-cart me-1"></i>
|
||||||
|
خرید
|
||||||
|
</span>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="intro-content">
|
||||||
|
<div class="features-section">
|
||||||
|
<h2>امکانات افزونه</h2>
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-barcode"></i></div>
|
||||||
|
<h3>مدیریت سریال گارانتی</h3>
|
||||||
|
<p>ثبت/ویرایش سریال، جستجو و فیلتر بر اساس وضعیت، تاریخ شروع/پایان و کالا</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-file-import"></i></div>
|
||||||
|
<h3>واردسازی انبوه با پیشنمایش</h3>
|
||||||
|
<p>پیشنمایش فایل و واردسازی انبوه سریالها (CSV/Excel) با اعتبارسنجی</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-link"></i></div>
|
||||||
|
<h3>تخصیص خودکار به فروش</h3>
|
||||||
|
<p>تخصیص سریالهای «آزاد» به اقلام حواله خروج بر اساس کالا و تعداد موردنیاز</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-qrcode"></i></div>
|
||||||
|
<h3>اسکن و تأیید انبار</h3>
|
||||||
|
<p>اسکن سریال در خروج از انبار و تأیید صحت کالا پیش از خروج (Verified)</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-sms"></i></div>
|
||||||
|
<h3>ارسال سریال برای خریدار</h3>
|
||||||
|
<p>ارسال سریالهای تخصیصیافته فاکتور به خریدار از طریق پیامک</p>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="feature-card">
|
||||||
|
<div class="feature-icon"><i class="fas fa-chart-pie"></i></div>
|
||||||
|
<h3>آمار و وضعیتها</h3>
|
||||||
|
<p>گزارش تعداد سریالها بر اساس وضعیت و شمارش سریالهای منقضیشده</p>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="invoice-types-section">
|
||||||
|
<h2>چرخه وضعیت سریال</h2>
|
||||||
|
<div class="invoice-types-grid">
|
||||||
|
<div class="invoice-type-card">
|
||||||
|
<div class="type-icon info"><i class="fas fa-dot-circle"></i></div>
|
||||||
|
<h4>آزاد</h4>
|
||||||
|
<p>سریال ثبت شده و آماده تخصیص به فروش است</p>
|
||||||
|
</div>
|
||||||
|
<div class="invoice-type-card">
|
||||||
|
<div class="type-icon primary"><i class="fas fa-link"></i></div>
|
||||||
|
<h4>تخصیص یافته</h4>
|
||||||
|
<p>سریال به فاکتور/سند فروش اختصاص یافته است</p>
|
||||||
|
</div>
|
||||||
|
<div class="invoice-type-card">
|
||||||
|
<div class="type-icon danger"><i class="fas fa-ban"></i></div>
|
||||||
|
<h4>باطل</h4>
|
||||||
|
<p>سریال نامعتبر یا ابطال شده است</p>
|
||||||
|
</div>
|
||||||
|
<div class="invoice-type-card">
|
||||||
|
<div class="type-icon info"><i class="fas fa-check-circle"></i></div>
|
||||||
|
<h4>مصرفشده</h4>
|
||||||
|
<p>سریال استفاده شده و قابل تخصیص مجدد نیست</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="validation-section">
|
||||||
|
<h2>فعالسازی توسط مشتری</h2>
|
||||||
|
<div class="validation-list">
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>مسیر عمومی فعالسازی: ارائه کد گارانتی توسط مشتری و اعتبارسنجی</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>مهلت فعالسازی قابل تنظیم</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>پشتیبانی از کد/رمز فعالسازی حواله برای امنیت بیشتر</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="validation-section">
|
||||||
|
<h2>کنترلها</h2>
|
||||||
|
<div class="validation-list">
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>جلوگیری از تخصیص تکراری سریال</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>همخوانی نوع کالا با سریال</span>
|
||||||
|
</div>
|
||||||
|
<div class="validation-item">
|
||||||
|
<i class="fas fa-check text-success"></i>
|
||||||
|
<span>مدیریت تاریخ شروع و پایان گارانتی</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setup-section">
|
||||||
|
<h2>مراحل راهاندازی</h2>
|
||||||
|
<div class="setup-steps">
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>فعالسازی افزونه</h4>
|
||||||
|
<p>افزونه را از مرکز افزونهها فعال کنید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>واردسازی سریالها</h4>
|
||||||
|
<p>سریالها را به صورت دستی یا انبوه اضافه کنید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setup-step">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div class="step-content">
|
||||||
|
<h4>تخصیص به حواله خروج</h4>
|
||||||
|
<p>سریالها را به اقلام حواله خروج تخصیص دهید</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="intro-footer">
|
||||||
|
<div style="margin-top: 20px; font-size: 0.75rem; color: #888; text-align: center;">
|
||||||
|
Developed by <a href="https://pirouz.xyz" target="_blank" style="color: #667eea; text-decoration: none;">Mohammad Rezai</a> – 2025
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WarrantyIntro',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
plugins: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isPluginActive(plugName) {
|
||||||
|
return this.plugins && this.plugins[plugName] !== undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.post('/api/plugin/get/actives').then((response) => {
|
||||||
|
this.plugins = response.data;
|
||||||
|
});
|
||||||
|
if (this.$store) {
|
||||||
|
this.$store.commit('setPageTitle', 'مدیریت گارانتی - معرفی افزونه')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* same styles as taxsettings intro for consistent look */
|
||||||
|
.plugin-intro { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
||||||
|
.intro-header { display: flex; align-items: center; gap: 20px; margin-bottom: 40px; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white; }
|
||||||
|
.plugin-icon { font-size: 3rem; background: rgba(255, 255, 255, 0.2); width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
.plugin-info h1 { margin: 0 0 10px 0; font-size: 2.5rem; font-weight: 700; }
|
||||||
|
.plugin-description { font-size: 1.1rem; margin: 0 0 15px 0; opacity: 0.9; }
|
||||||
|
.plugin-version { display: flex; gap: 10px; }
|
||||||
|
.version-badge, .status-badge { padding: 5px 12px; border-radius: 20px; font-size: 0.9rem; font-weight: 600; }
|
||||||
|
.version-badge { background: rgba(255, 255, 255, 0.2); }
|
||||||
|
.status-badge.active { background: #28a745; }
|
||||||
|
.intro-content { display: flex; flex-direction: column; gap: 40px; }
|
||||||
|
.features-section, .invoice-types-section, .validation-section, .setup-section, .compliance-section { background: white; border-radius: 15px; padding: 30px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }
|
||||||
|
.features-section h2, .invoice-types-section h2, .validation-section h2, .setup-section h2, .compliance-section h2 { margin: 0 0 25px 0; font-size: 1.8rem; color: #333; border-bottom: 3px solid #667eea; padding-bottom: 10px; }
|
||||||
|
.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 25px; }
|
||||||
|
.feature-card { padding: 25px; border-radius: 12px; background: #f8f9fa; border-left: 4px solid #667eea; transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
||||||
|
.feature-card:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); }
|
||||||
|
.feature-icon { font-size: 2rem; color: #667eea; margin-bottom: 15px; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; background: rgba(102, 126, 234, 0.1); border-radius: 50%; }
|
||||||
|
.invoice-types-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; }
|
||||||
|
.invoice-type-card { text-align: center; padding: 25px; border-radius: 12px; background: #f8f9fa; border: 2px solid transparent; transition: all 0.3s ease; }
|
||||||
|
.invoice-type-card:hover { border-color: #667eea; transform: translateY(-3px); }
|
||||||
|
.type-icon { font-size: 2.5rem; margin-bottom: 15px; width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; border-radius: 50%; margin: 0 auto 15px auto; }
|
||||||
|
.type-icon.primary { background: #e3f2fd; color: #1976d2; }
|
||||||
|
.type-icon.warning { background: #fff3e0; color: #f57c00; }
|
||||||
|
.type-icon.info { background: #e8f5e8; color: #388e3c; }
|
||||||
|
.validation-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; }
|
||||||
|
.validation-item { display: flex; align-items: center; gap: 12px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-right: 4px solid #28a745; }
|
||||||
|
.setup-steps { display: flex; flex-direction: column; gap: 20px; }
|
||||||
|
.setup-step { display: flex; align-items: flex-start; gap: 20px; padding: 20px; background: #f8f9fa; border-radius: 12px; border-right: 4px solid #667eea; }
|
||||||
|
.step-number { background: #667eea; color: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.2rem; flex-shrink: 0; }
|
||||||
|
.action-buttons { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; }
|
||||||
|
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; border: none; cursor: pointer; }
|
||||||
|
.btn-success { background: #28a745; color: white; }
|
||||||
|
.btn-success:hover { background: #218838; transform: translateY(-2px); }
|
||||||
|
@media (max-width: 768px) { .plugin-intro { padding: 15px; } .intro-header { flex-direction: column; text-align: center; padding: 20px; gap: 15px; } .plugin-icon { width: 60px; height: 60px; font-size: 2rem; } .plugin-info h1 { font-size: 1.8rem; } .features-section, .invoice-types-section, .validation-section, .setup-section, .compliance-section { padding: 20px; } .features-grid { grid-template-columns: 1fr; gap: 20px; } .feature-card { padding: 20px; } .feature-icon { width: 50px; height: 50px; font-size: 1.5rem; } .invoice-types-grid { grid-template-columns: 1fr; gap: 15px; } .invoice-type-card { padding: 20px; } .type-icon { width: 60px; height: 60px; font-size: 2rem; } .action-buttons { flex-direction: column; gap: 15px; } .btn { justify-content: center; padding: 15px 20px; font-size: 1rem; } }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<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" />
|
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||||
|
icon="mdi-arrow-right" />
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
@ -21,14 +22,8 @@
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip v-if="isPluginActive('taxsettings')" text="ارسال گروهی به کارپوشه مودیان" location="bottom">
|
<v-tooltip v-if="isPluginActive('taxsettings')" text="ارسال گروهی به کارپوشه مودیان" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn
|
<v-btn v-bind="props" icon="mdi-cloud-upload" color="orange" @click="sendBulkToTaxSystem()"
|
||||||
v-bind="props"
|
:disabled="itemsSelected.length === 0" :loading="bulkLoading"></v-btn>
|
||||||
icon="mdi-cloud-upload"
|
|
||||||
color="orange"
|
|
||||||
@click="sendBulkToTaxSystem()"
|
|
||||||
:disabled="itemsSelected.length === 0"
|
|
||||||
:loading="bulkLoading"
|
|
||||||
></v-btn>
|
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-menu>
|
<v-menu>
|
||||||
|
@ -46,7 +41,8 @@
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-subheader color="primary">{{ $t('dialog.change_labels') }}</v-list-subheader>
|
<v-list-subheader color="primary">{{ $t('dialog.change_labels') }}</v-list-subheader>
|
||||||
<v-list-item v-for="item in types" class="text-dark" :title="$t('dialog.change_to') + ' ' + item.label" @click="changeLabel(item)">
|
<v-list-item v-for="item in types" class="text-dark" :title="$t('dialog.change_to') + ' ' + item.label"
|
||||||
|
@click="changeLabel(item)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-label"></v-icon>
|
<v-icon color="green-darken-4" icon="mdi-label"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -72,7 +68,8 @@
|
||||||
<v-tab value="pending">فاکتورهای در انتظار تایید</v-tab>
|
<v-tab value="pending">فاکتورهای در انتظار تایید</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</div>
|
</div>
|
||||||
<v-text-field hide-details color="green" class="pt-0 rounded-0 mb-0" density="compact" :placeholder="$t('dialog.search_txt')" v-model="searchValue" type="text" clearable>
|
<v-text-field hide-details color="green" class="pt-0 rounded-0 mb-0" density="compact"
|
||||||
|
:placeholder="$t('dialog.search_txt')" v-model="searchValue" type="text" clearable>
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
|
@ -102,33 +99,23 @@
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-select class="py-2 my-2" v-model="dateFilter" :items="dateFilterOptions" label="فیلتر تاریخ" @update:model-value="filterTable" dense />
|
<v-select class="py-2 my-2" v-model="dateFilter" :items="dateFilterOptions" label="فیلتر تاریخ"
|
||||||
|
@update:model-value="filterTable" dense />
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</template>
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
<v-data-table-server
|
<v-data-table-server v-model:items-per-page="serverOptions.rowsPerPage" v-model:page="serverOptions.page"
|
||||||
v-model:items-per-page="serverOptions.rowsPerPage"
|
:headers="visibleHeaders" :items="displayItems" :items-length="displayTotal" :loading="loading"
|
||||||
v-model:page="serverOptions.page"
|
:no-data-text="$t('table.no_data')" v-model="itemsSelected" v-model:expanded="expanded"
|
||||||
:headers="visibleHeaders"
|
@update:options="updateServerOptions" show-select class="elevation-1 data-table-wrapper" item-value="code"
|
||||||
:items="displayItems"
|
:max-height="tableHeight" :header-props="{ class: 'custom-header' }" @update:expanded="onExpandedUpdate"
|
||||||
:items-length="displayTotal"
|
multi-sort>
|
||||||
:loading="loading"
|
|
||||||
:no-data-text="$t('table.no_data')"
|
|
||||||
v-model="itemsSelected"
|
|
||||||
v-model:expanded="expanded"
|
|
||||||
@update:options="updateServerOptions"
|
|
||||||
show-select
|
|
||||||
class="elevation-1 data-table-wrapper"
|
|
||||||
item-value="code"
|
|
||||||
:max-height="tableHeight"
|
|
||||||
:header-props="{ class: 'custom-header' }"
|
|
||||||
@update:expanded="onExpandedUpdate"
|
|
||||||
multi-sort
|
|
||||||
>
|
|
||||||
<template v-slot:item.expand="{ item }">
|
<template v-slot:item.expand="{ item }">
|
||||||
<v-btn variant="text" size="small" color="primary" :icon="expanded.includes(item.code) ? 'mdi-chevron-up' : 'mdi-chevron-down'" @click.stop="toggleExpand(item.code)" />
|
<v-btn variant="text" size="small" color="primary"
|
||||||
|
:icon="expanded.includes(item.code) ? 'mdi-chevron-up' : 'mdi-chevron-down'"
|
||||||
|
@click.stop="toggleExpand(item.code)" />
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item.receivedAmount="{ item }">
|
<template v-slot:item.receivedAmount="{ item }">
|
||||||
<span class="text-dark">
|
<span class="text-dark">
|
||||||
|
@ -141,7 +128,8 @@
|
||||||
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
|
<v-btn variant="text" size="small" color="error" icon="mdi-menu" v-bind="props" />
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item class="text-dark" :title="$t('dialog.accounting_doc')" :to="'/acc/accounting/view/' + item.code">
|
<v-list-item class="text-dark" :title="$t('dialog.accounting_doc')"
|
||||||
|
:to="'/acc/accounting/view/' + item.code">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-file"></v-icon>
|
<v-icon color="green-darken-4" icon="mdi-file"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -151,17 +139,20 @@
|
||||||
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
|
<v-icon color="green-darken-4" icon="mdi-eye"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item class="text-dark" :title="$t('dialog.export_pdf')" @click="printOptions.selectedPrintCode = item.code; modal = true;">
|
<v-list-item class="text-dark" :title="$t('dialog.export_pdf')"
|
||||||
|
@click="printOptions.selectedPrintCode = item.code; modal = true;">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
<v-icon icon="mdi-file-pdf-box"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item v-if="isPluginActive('taxsettings')" class="text-dark" :title="$t('dialog.send_to_tax_system')" @click="sendToTaxSystem(item.code)">
|
<v-list-item v-if="isPluginActive('taxsettings') && (item.isApproved || !item.isPreview)" class="text-dark"
|
||||||
|
:title="$t('dialog.send_to_tax_system')" @click="sendToTaxSystem(item.code)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="orange" icon="mdi-cloud-upload"></v-icon>
|
<v-icon color="orange" icon="mdi-cloud-upload"></v-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item v-if="canShowApprovalButton(item)" class="text-dark" title="تایید فاکتور" @click="approveInvoice(item.code)">
|
<v-list-item v-if="canShowApprovalButton(item)" class="text-dark" title="تایید فاکتور"
|
||||||
|
@click="approveInvoice(item.code)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="success">mdi-check-decagram</v-icon>
|
<v-icon color="success">mdi-check-decagram</v-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -240,7 +231,8 @@
|
||||||
<template v-slot:expanded-row="{ item }">
|
<template v-slot:expanded-row="{ item }">
|
||||||
<tr>
|
<tr>
|
||||||
<td :colspan="visibleHeaders.length" class="expanded-row">
|
<td :colspan="visibleHeaders.length" class="expanded-row">
|
||||||
<v-progress-circular v-if="loading && expanded.includes(item.code)" indeterminate color="primary" class="my-2" />
|
<v-progress-circular v-if="loading && expanded.includes(item.code)" indeterminate color="primary"
|
||||||
|
class="my-2" />
|
||||||
<v-list v-else dense class="expanded-list">
|
<v-list v-else dense class="expanded-list">
|
||||||
<v-list-item v-for="row in item.rows" :key="row.id">
|
<v-list-item v-for="row in item.rows" :key="row.id">
|
||||||
<v-list-item-subtitle>
|
<v-list-item-subtitle>
|
||||||
|
@ -270,7 +262,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-item">
|
<div class="summary-item">
|
||||||
<span class="summary-label">جمع سود موارد انتخاب شده:</span>
|
<span class="summary-label">جمع سود موارد انتخاب شده:</span>
|
||||||
<span class="summary-value" :class="{'text-success': sumSelectedProfit >= 0, 'text-danger': sumSelectedProfit < 0}">
|
<span class="summary-value"
|
||||||
|
:class="{ 'text-success': sumSelectedProfit >= 0, 'text-danger': sumSelectedProfit < 0 }">
|
||||||
{{ $filters.formatNumber(Math.abs(sumSelectedProfit)) }}
|
{{ $filters.formatNumber(Math.abs(sumSelectedProfit)) }}
|
||||||
<span v-if="sumSelectedProfit < 0">(زیان)</span>
|
<span v-if="sumSelectedProfit < 0">(زیان)</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -289,7 +282,8 @@
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="header in allHeaders" :key="header.value" cols="12" sm="4" class="my-0 py-0">
|
<v-col v-for="header in allHeaders" :key="header.value" cols="12" sm="4" class="my-0 py-0">
|
||||||
<v-checkbox v-model="header.visible" :label="header.title" @update:model-value="updateColumnVisibility" hide-details="auto" />
|
<v-checkbox v-model="header.visible" :label="header.title" @update:model-value="updateColumnVisibility"
|
||||||
|
hide-details="auto" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
@ -322,39 +316,46 @@
|
||||||
<v-col cols="12" sm="6">
|
<v-col cols="12" sm="6">
|
||||||
<v-tooltip :text="$t('dialog.bid_info_label')" location="right">
|
<v-tooltip :text="$t('dialog.bid_info_label')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-bind="props" inset v-model="printOptions.bidInfo" color="primary" :label="$t('dialog.bid_info_label')" hide-details></v-switch>
|
<v-switch v-bind="props" inset v-model="printOptions.bidInfo" color="primary"
|
||||||
|
:label="$t('dialog.bid_info_label')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip :text="$t('dialog.invoice_pays')" location="right">
|
<v-tooltip :text="$t('dialog.invoice_pays')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-bind="props" inset v-model="printOptions.pays" color="primary" :label="$t('dialog.invoice_pays')" hide-details></v-switch>
|
<v-switch v-bind="props" inset v-model="printOptions.pays" color="primary"
|
||||||
|
:label="$t('dialog.invoice_pays')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip :text="$t('dialog.invoice_footer_note')" location="right">
|
<v-tooltip :text="$t('dialog.invoice_footer_note')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-bind="props" inset v-model="printOptions.note" color="primary" :label="$t('dialog.invoice_footer_note')" hide-details></v-switch>
|
<v-switch v-bind="props" inset v-model="printOptions.note" color="primary"
|
||||||
|
:label="$t('dialog.invoice_footer_note')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" sm="6">
|
<v-col cols="12" sm="6">
|
||||||
<v-tooltip :text="$t('dialog.tax_dexpo')" location="right">
|
<v-tooltip :text="$t('dialog.tax_dexpo')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-bind="props" inset v-model="printOptions.taxInfo" color="primary" :label="$t('dialog.tax_dexpo')" hide-details></v-switch>
|
<v-switch v-bind="props" inset v-model="printOptions.taxInfo" color="primary"
|
||||||
|
:label="$t('dialog.tax_dexpo')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip :text="$t('dialog.discount_dexpo')" location="right">
|
<v-tooltip :text="$t('dialog.discount_dexpo')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-bind="props" inset v-model="printOptions.discountInfo" color="primary" :label="$t('dialog.discount_dexpo')" hide-details></v-switch>
|
<v-switch v-bind="props" inset v-model="printOptions.discountInfo" color="primary"
|
||||||
|
:label="$t('dialog.discount_dexpo')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip :text="$t('dialog.business_stamp')" location="right">
|
<v-tooltip :text="$t('dialog.business_stamp')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-if="isPluginActive('accpro')" v-bind="props" inset v-model="printOptions.businessStamp" color="primary" :label="$t('dialog.business_stamp')" hide-details></v-switch>
|
<v-switch v-if="isPluginActive('accpro')" v-bind="props" inset v-model="printOptions.businessStamp"
|
||||||
|
color="primary" :label="$t('dialog.business_stamp')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-tooltip :text="$t('dialog.invoice_index')" location="right">
|
<v-tooltip :text="$t('dialog.invoice_index')" location="right">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-switch v-if="isPluginActive('accpro')" v-bind="props" inset v-model="printOptions.invoiceIndex" color="primary" :label="$t('dialog.invoice_index')" hide-details></v-switch>
|
<v-switch v-if="isPluginActive('accpro')" v-bind="props" inset v-model="printOptions.invoiceIndex"
|
||||||
|
color="primary" :label="$t('dialog.invoice_index')" hide-details></v-switch>
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
@ -593,7 +594,7 @@ export default defineComponent({
|
||||||
this.items = all;
|
this.items = all;
|
||||||
this.total = Number(response.data.total) || 0;
|
this.total = Number(response.data.total) || 0;
|
||||||
if (this.business.requireTwoStepApproval) {
|
if (this.business.requireTwoStepApproval) {
|
||||||
this.itemsApproved = all.filter(i => i.isApproved === true);
|
this.itemsApproved = all.filter(i => i.isApproved === true || (!i.isPreview && !i.isApproved));
|
||||||
this.itemsPending = all.filter(i => i.isPreview === true && i.isApproved !== true);
|
this.itemsPending = all.filter(i => i.isPreview === true && i.isApproved !== true);
|
||||||
this.totalApproved = this.itemsApproved.length;
|
this.totalApproved = this.itemsApproved.length;
|
||||||
this.totalPending = this.itemsPending.length;
|
this.totalPending = this.itemsPending.length;
|
||||||
|
@ -752,21 +753,37 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('خطا در دریافت فایل PDF:', error);
|
console.error('خطا در دریافت فایل PDF:', error);
|
||||||
|
if (error?.response?.data?.message) {
|
||||||
|
Swal.fire({
|
||||||
|
text: error?.response?.data?.message,
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
text: 'خطا در دریافت فایل PDF',
|
text: 'خطا در دریافت فایل PDF',
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
confirmButtonText: 'قبول'
|
confirmButtonText: 'قبول'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
if (error?.response?.data?.message) {
|
||||||
|
Swal.fire({
|
||||||
|
text: error?.response?.data?.message,
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
text: 'خطا در ایجاد نسخه PDF',
|
text: 'خطا در ایجاد نسخه PDF',
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
confirmButtonText: 'قبول'
|
confirmButtonText: 'قبول'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteItem(code) {
|
deleteItem(code) {
|
||||||
|
@ -933,6 +950,17 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedInvoices = this.items.filter(inv => this.itemsSelected.includes(inv.code));
|
||||||
|
|
||||||
|
if (selectedInvoices.some(inv => !inv.isApproved && inv.isPreview)) {
|
||||||
|
Swal.fire({
|
||||||
|
text: 'بعضی از فاکتورهای انتخابی هنوز تایید نشدهاند و قابل ارسال به سامانه مودیان نیستند.',
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const selectedCount = this.itemsSelected.length;
|
const selectedCount = this.itemsSelected.length;
|
||||||
|
|
||||||
const result = await Swal.fire({
|
const result = await Swal.fire({
|
||||||
|
|
|
@ -24,7 +24,10 @@
|
||||||
<v-tab value="2">
|
<v-tab value="2">
|
||||||
{{ $t('dialog.global_settings') }}
|
{{ $t('dialog.global_settings') }}
|
||||||
</v-tab>
|
</v-tab>
|
||||||
<v-tab value="3" v-if="showBackupTab">
|
<v-tab value="3" v-if="isPluginActive('warranty')">
|
||||||
|
{{ $t('dialog.warranty_settings') }}
|
||||||
|
</v-tab>
|
||||||
|
<v-tab value="4" v-if="showBackupTab">
|
||||||
نسخه پشتیبان
|
نسخه پشتیبان
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
|
@ -195,6 +198,63 @@
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-tabs-window-item>
|
</v-tabs-window-item>
|
||||||
|
<v-tabs-window-item value="3" v-if="isPluginActive('warranty')">
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
<h3 class="text-primary mb-6">تنظیمات گارانتی</h3>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="8">
|
||||||
|
<v-card variant="outlined" class="mb-6">
|
||||||
|
<v-card-title class="text-h6 text-primary">
|
||||||
|
<v-icon icon="mdi-shield-lock-outline" class="mr-2"></v-icon>
|
||||||
|
رفتار گارانتی در حواله خروج
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-switch
|
||||||
|
v-model="content.requireWarrantyOnDelivery"
|
||||||
|
label="الزام ثبت گارانتی هنگام صدور حواله خروج"
|
||||||
|
color="primary"
|
||||||
|
hide-details
|
||||||
|
class="mb-2"
|
||||||
|
/>
|
||||||
|
<v-switch
|
||||||
|
v-model="content.matchWarrantyToSerial"
|
||||||
|
label="تطبیق خودکار گارانتی با سریال کالا (در صورت یکسان بودن کد)"
|
||||||
|
color="primary"
|
||||||
|
hide-details
|
||||||
|
class="mb-2"
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
v-model.number="content.activationGraceDays"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
label="مهلت فعالسازی گارانتی (روز)"
|
||||||
|
variant="outlined"
|
||||||
|
density="compact"
|
||||||
|
hide-details
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="4">
|
||||||
|
<v-card variant="outlined">
|
||||||
|
<v-card-title class="text-h6 text-primary">
|
||||||
|
<v-icon icon="mdi-information-outline" class="mr-2"></v-icon>
|
||||||
|
نکات
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item title="این تنظیمات برای همین کسبوکار ذخیره میشود." />
|
||||||
|
<v-list-item title="در صورت فعال بودن الزام، ثبت حواله بدون سریال مجاز نیست." />
|
||||||
|
<v-list-item title="مهلت فعالسازی فقط برای مسیر فعالسازی مشتری استفاده میشود." />
|
||||||
|
</v-list>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-tabs-window-item>
|
||||||
<v-tabs-window-item value="1">
|
<v-tabs-window-item value="1">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
|
@ -534,7 +594,7 @@
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-tabs-window-item>
|
</v-tabs-window-item>
|
||||||
<v-tabs-window-item value="3" v-if="showBackupTab">
|
<v-tabs-window-item value="4" v-if="showBackupTab">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<h3 class="text-primary mb-4">نسخه پشتیبان از اطلاعات کسب و کار</h3>
|
<h3 class="text-primary mb-4">نسخه پشتیبان از اطلاعات کسب و کار</h3>
|
||||||
|
@ -693,7 +753,10 @@ export default {
|
||||||
},
|
},
|
||||||
updateSellPrice: false,
|
updateSellPrice: false,
|
||||||
updateBuyPrice: false,
|
updateBuyPrice: false,
|
||||||
profitCalcType: 'lis'
|
profitCalcType: 'lis',
|
||||||
|
warrantyRequireOnDelivery: false,
|
||||||
|
warrantyActivationGraceDays: 7,
|
||||||
|
warrantyMatchToSerial: false
|
||||||
},
|
},
|
||||||
users: [],
|
users: [],
|
||||||
listBanks: [],
|
listBanks: [],
|
||||||
|
@ -814,7 +877,10 @@ export default {
|
||||||
'year': this.content.year,
|
'year': this.content.year,
|
||||||
'commodityUpdateBuyPriceAuto': this.content.updateBuyPrice,
|
'commodityUpdateBuyPriceAuto': this.content.updateBuyPrice,
|
||||||
'commodityUpdateSellPriceAuto': this.content.updateSellPrice,
|
'commodityUpdateSellPriceAuto': this.content.updateSellPrice,
|
||||||
'profitCalcType': this.content.profitCalcType
|
'profitCalcType': this.content.profitCalcType,
|
||||||
|
'requireWarrantyOnDelivery': this.content.requireWarrantyOnDelivery,
|
||||||
|
'activationGraceDays': this.content.activationGraceDays,
|
||||||
|
'matchWarrantyToSerial': this.content.matchWarrantyToSerial
|
||||||
};
|
};
|
||||||
|
|
||||||
axios.post('/api/business/insert', data)
|
axios.post('/api/business/insert', data)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ref, onMounted, computed } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import moment from 'jalali-moment'
|
import moment from 'jalali-moment'
|
||||||
|
import Swal from 'sweetalert2'
|
||||||
|
|
||||||
interface Business {
|
interface Business {
|
||||||
legal_name: string
|
legal_name: string
|
||||||
|
@ -136,6 +137,13 @@ const printInvoice = async () => {
|
||||||
})
|
})
|
||||||
window.open(`${import.meta.env.VITE_API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer')
|
window.open(`${import.meta.env.VITE_API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error?.response?.data?.message) {
|
||||||
|
Swal.fire({
|
||||||
|
text: error?.response?.data?.message,
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'قبول'
|
||||||
|
});
|
||||||
|
}
|
||||||
console.error('Error printing invoice:', error)
|
console.error('Error printing invoice:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue