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 @@ + + + + + +