diff --git a/hesabixCore/src/Controller/PreinvoiceController.php b/hesabixCore/src/Controller/PreinvoiceController.php
index 5021363..ef930a2 100644
--- a/hesabixCore/src/Controller/PreinvoiceController.php
+++ b/hesabixCore/src/Controller/PreinvoiceController.php
@@ -19,6 +19,7 @@ use App\Entity\StoreroomTicket;
use App\Service\Printers;
use App\Service\registryMGR;
use App\Service\SMS;
+use App\Service\PreInvoiceConversionService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -385,4 +386,109 @@ class PreinvoiceController extends AbstractController
return $this->json(['id' => $pdfPid]);
}
+
+ #[Route('/api/preinvoice/convert-to-invoice/{code}', name: 'app_preinvoice_convert_to_invoice', methods: ['POST'])]
+ public function convertToInvoice(
+ string $code,
+ Request $request,
+ Access $access,
+ PreInvoiceConversionService $conversionService,
+ EntityManagerInterface $entityManager
+ ): JsonResponse {
+ $acc = $access->hasRole('plugAccproPresell');
+ if (!$acc) {
+ return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
+ }
+
+ // پیدا کردن پیشفاکتور
+ $preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy([
+ 'bid' => $acc['bid'],
+ 'code' => $code,
+ 'year' => $acc['year']
+ ]);
+
+ if (!$preinvoice) {
+ return new JsonResponse($this->extractor->operationFail('پیشفاکتور یافت نشد'), 404);
+ }
+
+ // بررسی امکان تبدیل
+ $canConvert = $conversionService->canConvert($preinvoice);
+ if (!$canConvert['canConvert']) {
+ return new JsonResponse($this->extractor->operationFail(implode(', ', $canConvert['errors'])), 400);
+ }
+
+ // انجام تبدیل
+ $result = $conversionService->convertToInvoice($preinvoice, $this->getUser(), $acc);
+
+ if ($result['success']) {
+ return new JsonResponse($result);
+ } else {
+ return new JsonResponse($this->extractor->operationFail($result['message']), 500);
+ }
+ }
+
+ #[Route('/api/preinvoice/can-convert/{code}', name: 'app_preinvoice_can_convert', methods: ['GET'])]
+ public function canConvertToInvoice(
+ string $code,
+ Access $access,
+ PreInvoiceConversionService $conversionService,
+ EntityManagerInterface $entityManager
+ ): JsonResponse {
+ $acc = $access->hasRole('plugAccproPresell');
+ if (!$acc) {
+ return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
+ }
+
+ // پیدا کردن پیشفاکتور
+ $preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy([
+ 'bid' => $acc['bid'],
+ 'code' => $code,
+ 'year' => $acc['year']
+ ]);
+
+ if (!$preinvoice) {
+ return new JsonResponse($this->extractor->operationFail('پیشفاکتور یافت نشد'), 404);
+ }
+
+ // بررسی امکان تبدیل
+ $result = $conversionService->canConvert($preinvoice);
+
+ return new JsonResponse([
+ 'canConvert' => $result['canConvert'],
+ 'errors' => $result['errors']
+ ]);
+ }
+
+ #[Route('/api/preinvoice/test-values/{code}', name: 'app_preinvoice_test_values', methods: ['GET'])]
+ public function testPreInvoiceValues(
+ string $code,
+ Access $access,
+ EntityManagerInterface $entityManager
+ ): JsonResponse {
+ $acc = $access->hasRole('plugAccproPresell');
+ if (!$acc) {
+ return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
+ }
+
+ // پیدا کردن پیشفاکتور
+ $preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy([
+ 'bid' => $acc['bid'],
+ 'code' => $code,
+ 'year' => $acc['year']
+ ]);
+
+ if (!$preinvoice) {
+ return new JsonResponse($this->extractor->operationFail('پیشفاکتور یافت نشد'), 404);
+ }
+
+ return new JsonResponse([
+ 'totalDiscount' => $preinvoice->getTotalDiscount(),
+ 'totalDiscountPercent' => $preinvoice->getTotalDiscountPercent(),
+ 'shippingCost' => $preinvoice->getShippingCost(),
+ 'showTotalPercentDiscount' => $preinvoice->isShowTotalPercentDiscount(),
+ 'amount' => $preinvoice->getAmount(),
+ 'totalDiscountFloat' => floatval($preinvoice->getTotalDiscount() ?: 0),
+ 'shippingCostFloat' => floatval($preinvoice->getShippingCost() ?: 0)
+ ]);
+ }
}
diff --git a/hesabixCore/src/Service/Log.php b/hesabixCore/src/Service/Log.php
index 165e51f..8fb8841 100644
--- a/hesabixCore/src/Service/Log.php
+++ b/hesabixCore/src/Service/Log.php
@@ -36,4 +36,24 @@ class Log
$this->em->persist($log);
$this->em->flush();
}
+
+ /**
+ * ثبت لاگ برای عملیات پیشفاکتور
+ */
+ public function insertPreInvoiceLog(string $part, string $des, User | null $user = null, Business | string | null $bid = null): void
+ {
+ if(is_string($bid))
+ $bid = $this->em->getRepository(Business::class)->find($bid);
+ $log = new \App\Entity\Log();
+ $log->setDateSubmit(time());
+ $log->setPart($part);
+ $log->setDes($des);
+ $log->setUser($user);
+ $log->setBid($bid);
+ $log->setDoc(null); // برای پیشفاکتور، doc را null قرار میدهیم
+ $log->setRepserviceOrder(null);
+ $log->setIpaddress($this->remoteAddress->getIpAddress());
+ $this->em->persist($log);
+ $this->em->flush();
+ }
}
\ No newline at end of file
diff --git a/hesabixCore/src/Service/PreInvoiceConversionService.php b/hesabixCore/src/Service/PreInvoiceConversionService.php
new file mode 100644
index 0000000..71efa31
--- /dev/null
+++ b/hesabixCore/src/Service/PreInvoiceConversionService.php
@@ -0,0 +1,285 @@
+entityManager = $entityManager;
+ $this->provider = $provider;
+ $this->log = $log;
+ }
+
+ /**
+ * تبدیل پیشفاکتور به فاکتور فروش
+ */
+ public function convertToInvoice(PreInvoiceDoc $preInvoice, UserInterface $user, array $acc): array
+ {
+ try {
+ // بررسی وضعیت پیشفاکتور
+ if ($preInvoice->getStatus() === 'converted') {
+ throw new \Exception('این پیشفاکتور قبلاً به فاکتور فروش تبدیل شده است');
+ }
+
+ // ایجاد فاکتور فروش جدید
+ $sellDoc = new HesabdariDoc();
+ $sellDoc->setBid($acc['bid']);
+ $sellDoc->setYear($acc['year']);
+ $sellDoc->setDateSubmit(time());
+ $sellDoc->setType('sell');
+ $sellDoc->setSubmitter($user);
+ $sellDoc->setMoney($acc['money']);
+ $sellDoc->setCode($this->provider->getAccountingCode($acc['bid'], 'accounting'));
+ $sellDoc->setDate($preInvoice->getDate());
+ $sellDoc->setDes($preInvoice->getDes() ?: 'فاکتور فروش تبدیل شده از پیشفاکتور شماره ' . $preInvoice->getCode());
+ // مبلغ کل موقت - بعداً بهروزرسانی میشود
+ $sellDoc->setAmount($preInvoice->getAmount());
+ $sellDoc->setTaxPercent($preInvoice->getTaxPercent() ?: 0);
+ $sellDoc->setStatus('approved');
+
+ // تنظیم تخفیف کل
+ if ($preInvoice->getTotalDiscount() || $preInvoice->getTotalDiscountPercent()) {
+ $sellDoc->setDiscountType($preInvoice->isShowTotalPercentDiscount() ? 'percent' : 'amount');
+ $sellDoc->setDiscountPercent(floatval($preInvoice->getTotalDiscountPercent() ?: 0));
+ }
+
+ // تنظیم برچسب فاکتور اگر وجود داشته باشد
+ if ($preInvoice->getInvoiceLabel()) {
+ $sellDoc->setInvoiceLabel($preInvoice->getInvoiceLabel());
+ }
+
+ // تنظیم فروشنده اگر وجود داشته باشد
+ if ($preInvoice->getSalesman()) {
+ $sellDoc->setSalesman($preInvoice->getSalesman());
+ }
+
+ $this->entityManager->persist($sellDoc);
+
+ // تبدیل آیتمهای پیشفاکتور به ردیفهای فاکتور فروش
+ $sumTotal = 0;
+ $sumTax = 0;
+ $totalItemDiscount = 0;
+
+ // تعریف متغیرهای تخفیف و هزینه حمل
+ $totalDiscountAmount = floatval($preInvoice->getTotalDiscount() ?: 0);
+ $shippingCost = floatval($preInvoice->getShippingCost() ?: 0);
+
+ foreach ($preInvoice->getPreInvoiceItems() as $preItem) {
+ $hesabdariRow = new HesabdariRow();
+ $hesabdariRow->setDes($preItem->getDes() ?: 'فروش کالا');
+ $hesabdariRow->setBid($acc['bid']);
+ $hesabdariRow->setYear($acc['year']);
+ $hesabdariRow->setDoc($sellDoc);
+
+ $itemTotal = $preItem->getCommodityCount() * $preItem->getBs();
+ $itemDiscount = $preItem->getDiscountAmount() ?: 0;
+
+ // محاسبه تخفیف درصدی اگر تخفیف مقداری صفر باشد
+ if ($itemDiscount == 0 && $preItem->getDiscountPercent() && $preItem->getDiscountPercent() > 0) {
+ $itemDiscount = ($itemTotal * floatval($preItem->getDiscountPercent())) / 100;
+ }
+
+ $itemTotalAfterDiscount = $itemTotal - $itemDiscount;
+
+ // ذخیره مبلغ کل کالا بعد از تخفیف در فیلد bs - مطابق با انتظار Controller
+ $hesabdariRow->setBs($itemTotalAfterDiscount);
+ $hesabdariRow->setBd(0);
+ $hesabdariRow->setCommdityCount($preItem->getCommodityCount());
+ $hesabdariRow->setCommodity($preItem->getCommodity());
+ $hesabdariRow->setDiscount($itemDiscount);
+ $hesabdariRow->setDiscountPercent($preItem->getDiscountPercent() ?: 0);
+ $hesabdariRow->setDiscountType($preItem->isShowPercentDiscount() ? 'percent' : 'amount');
+
+ // محاسبه مالیات
+ $taxAmount = 0;
+ if ($preInvoice->getTaxPercent() && $preInvoice->getTaxPercent() > 0) {
+ $taxAmount = ($itemTotalAfterDiscount * $preInvoice->getTaxPercent()) / 100;
+ $hesabdariRow->setTax($taxAmount);
+ $sumTax += $taxAmount;
+ }
+
+ // تنظیم مرجع حسابداری (جدول 1 برای فروش)
+ $ref = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '1']);
+ $hesabdariRow->setRef($ref);
+
+ $this->entityManager->persist($hesabdariRow);
+ $sumTotal += $itemTotalAfterDiscount;
+ $totalItemDiscount += $itemDiscount;
+ }
+
+ // محاسبه تخفیف درصدی اگر مبلغ تخفیف صفر باشد اما درصد تخفیف وجود داشته باشد
+ if ($totalDiscountAmount == 0 && $preInvoice->getTotalDiscountPercent() && $preInvoice->getTotalDiscountPercent() > 0) {
+ $totalDiscountAmount = ($sumTotal * floatval($preInvoice->getTotalDiscountPercent())) / 100;
+ }
+
+ // افزودن ردیف مالیات کل
+ if ($sumTax > 0) {
+ $taxRow = new HesabdariRow();
+ $taxRow->setDes('مالیات بر ارزش افزوده');
+ $taxRow->setBid($acc['bid']);
+ $taxRow->setYear($acc['year']);
+ $taxRow->setDoc($sellDoc);
+ $taxRow->setBs($sumTax);
+ $taxRow->setBd(0);
+ $taxRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '33']);
+ $taxRow->setRef($taxRef);
+ $this->entityManager->persist($taxRow);
+ }
+
+ // افزودن ردیف تخفیف کل
+ if ($totalDiscountAmount > 0) {
+ $discountRow = new HesabdariRow();
+ $discountRow->setDes('تخفیف کل');
+ $discountRow->setBid($acc['bid']);
+ $discountRow->setYear($acc['year']);
+ $discountRow->setDoc($sellDoc);
+ $discountRow->setBs(0);
+ $discountRow->setBd($totalDiscountAmount);
+ $discountRow->setDiscount($totalDiscountAmount);
+ $discountRow->setDiscountPercent(floatval($preInvoice->getTotalDiscountPercent() ?: 0));
+ $discountRow->setDiscountType($preInvoice->isShowTotalPercentDiscount() ? 'percent' : 'amount');
+ // استفاده از جدول تخفیف (کد 104) - مطابق با Controller فاکتور فروش
+ $discountRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '104']);
+ if (!$discountRef) {
+ // اگر جدول تخفیف وجود نداشت، از جدول 4 استفاده کن
+ $discountRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '4']);
+ if (!$discountRef) {
+ // اگر جدول 4 هم وجود نداشت، از جدول 1 استفاده کن
+ $discountRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '1']);
+ }
+ }
+ $discountRow->setRef($discountRef);
+ $this->entityManager->persist($discountRow);
+ }
+
+ // افزودن ردیف هزینه حمل
+ if ($shippingCost > 0) {
+ $shippingRow = new HesabdariRow();
+ $shippingRow->setDes('هزینه حمل و نقل');
+ $shippingRow->setBid($acc['bid']);
+ $shippingRow->setYear($acc['year']);
+ $shippingRow->setDoc($sellDoc);
+ $shippingRow->setBs($shippingCost);
+ $shippingRow->setBd(0);
+ // استفاده از جدول درآمد حمل کالا (کد 61) - مطابق با Controller فاکتور فروش
+ $shippingRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '61']);
+ if (!$shippingRef) {
+ // اگر جدول درآمد حمل کالا وجود نداشت، از جدول هزینه حمل کالا (کد 90) استفاده کن
+ $shippingRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '90']);
+ if (!$shippingRef) {
+ // اگر جدول 90 هم وجود نداشت، از جدول 1 استفاده کن
+ $shippingRef = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '1']);
+ }
+ }
+ $shippingRow->setRef($shippingRef);
+ $this->entityManager->persist($shippingRow);
+ }
+
+ // افزودن ردیف اصلی فاکتور (بدهکار به مشتری)
+ $mainRow = new HesabdariRow();
+ $mainRow->setDes('فاکتور فروش');
+ $mainRow->setBid($acc['bid']);
+ $mainRow->setYear($acc['year']);
+ $mainRow->setDoc($sellDoc);
+ $mainRow->setBs(0);
+ $mainRow->setBd($sumTotal + $sumTax + $shippingCost - $totalDiscountAmount);
+
+ // تنظیم مشتری
+ $mainRow->setPerson($preInvoice->getPerson());
+
+ // تنظیم مرجع حسابداری (جدول 3 برای بدهی به مشتری)
+ $ref = $this->entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
+ $mainRow->setRef($ref);
+
+ $this->entityManager->persist($mainRow);
+
+ // بهروزرسانی مبلغ کل فاکتور فروش
+ $finalAmount = $sumTotal + $sumTax + $shippingCost - $totalDiscountAmount;
+ $sellDoc->setAmount($finalAmount);
+ $this->entityManager->persist($sellDoc);
+
+ // ایجاد لینک کوتاه
+ $sellDoc->setShortlink($this->provider->RandomString(8));
+
+ // بهروزرسانی وضعیت پیشفاکتور
+ $preInvoice->setStatus('converted');
+ $this->entityManager->persist($preInvoice);
+
+ $this->entityManager->flush();
+
+ // ثبت لاگ برای عملیات پیشفاکتور
+ $this->log->insertPreInvoiceLog(
+ 'پیشفاکتور',
+ 'تبدیل پیشفاکتور شماره ' . $preInvoice->getCode() . ' به فاکتور فروش شماره ' . $sellDoc->getCode(),
+ $user,
+ $acc['bid']->getId()
+ );
+
+ // ثبت لاگ برای فاکتور فروش ایجاد شده
+ $this->log->insert(
+ 'فاکتور فروش',
+ 'فاکتور فروش شماره ' . $sellDoc->getCode() . ' از پیشفاکتور شماره ' . $preInvoice->getCode() . ' ایجاد شد',
+ $user,
+ $acc['bid']->getId(),
+ $sellDoc
+ );
+
+ return [
+ 'success' => true,
+ 'message' => 'پیشفاکتور با موفقیت به فاکتور فروش تبدیل شد',
+ 'invoiceCode' => $sellDoc->getCode(),
+ 'invoiceId' => $sellDoc->getId()
+ ];
+
+ } catch (\Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => 'خطا در تبدیل پیشفاکتور: ' . $e->getMessage()
+ ];
+ }
+ }
+
+ /**
+ * بررسی امکان تبدیل پیشفاکتور
+ */
+ public function canConvert(PreInvoiceDoc $preInvoice): array
+ {
+ $errors = [];
+
+ // بررسی وضعیت پیشفاکتور
+ if ($preInvoice->getStatus() === 'converted') {
+ $errors[] = 'این پیشفاکتور قبلاً به فاکتور فروش تبدیل شده است';
+ }
+
+ // بررسی وجود آیتمها
+ if ($preInvoice->getPreInvoiceItems()->count() === 0) {
+ $errors[] = 'پیشفاکتور باید حداقل یک آیتم داشته باشد';
+ }
+
+ // بررسی مشتری
+ if (!$preInvoice->getPerson()) {
+ $errors[] = 'مشتری برای پیشفاکتور تعیین نشده است';
+ }
+
+ return [
+ 'canConvert' => empty($errors),
+ 'errors' => $errors
+ ];
+ }
+}
\ No newline at end of file
diff --git a/webUI/src/views/acc/presell/list.vue b/webUI/src/views/acc/presell/list.vue
index 8a215cc..cf85bb3 100755
--- a/webUI/src/views/acc/presell/list.vue
+++ b/webUI/src/views/acc/presell/list.vue
@@ -59,6 +59,11 @@
+
+
+
+
+
@@ -353,6 +358,30 @@ export default {
if (typeof str != "string") return false;
return !isNaN(str) && !isNaN(parseFloat(str));
},
+ async convertToInvoice(code) {
+ try {
+ this.loading = true;
+ const response = await axios.post(`/api/preinvoice/convert-to-invoice/${code}`);
+
+ if (response.data.success) {
+ this.showSnackbar(response.data.message, 'success');
+ // بارگذاری مجدد دادهها
+ this.loadData();
+
+ // نمایش اطلاعات فاکتور ایجاد شده
+ setTimeout(() => {
+ this.showSnackbar(`فاکتور فروش شماره ${response.data.invoiceCode} ایجاد شد`, 'info');
+ }, 1000);
+ } else {
+ this.showSnackbar(response.data.message || 'خطا در تبدیل پیشفاکتور', 'error');
+ }
+ } catch (error) {
+ console.error('Error converting preinvoice to invoice:', error);
+ this.showSnackbar(error.response?.data?.message || 'خطا در تبدیل پیشفاکتور', 'error');
+ } finally {
+ this.loading = false;
+ }
+ },
},
beforeMount() {
this.loadData();
diff --git a/webUI/src/views/acc/presell/view.vue b/webUI/src/views/acc/presell/view.vue
index aa7db77..1dba307 100755
--- a/webUI/src/views/acc/presell/view.vue
+++ b/webUI/src/views/acc/presell/view.vue
@@ -12,6 +12,11 @@
+
+
+
+
+
@@ -174,6 +179,29 @@ export default defineComponent({
.then((response) => {
this.presellData = response.data;
});
+ },
+ async convertToInvoice() {
+ try {
+ const response = await axios.post(`/api/preinvoice/convert-to-invoice/${this.code}`);
+
+ if (response.data.success) {
+ // نمایش پیام موفقیت
+ this.$emit('show-snackbar', response.data.message, 'success');
+
+ // بستن دیالوگ
+ this.handleDialogClose(false);
+
+ // نمایش اطلاعات فاکتور ایجاد شده
+ setTimeout(() => {
+ this.$emit('show-snackbar', `فاکتور فروش شماره ${response.data.invoiceCode} ایجاد شد`, 'info');
+ }, 1000);
+ } else {
+ this.$emit('show-snackbar', response.data.message || 'خطا در تبدیل پیشفاکتور', 'error');
+ }
+ } catch (error) {
+ console.error('Error converting preinvoice to invoice:', error);
+ this.$emit('show-snackbar', error.response?.data?.message || 'خطا در تبدیل پیشفاکتور', 'error');
+ }
}
},
created() {