diff --git a/hesabixCore/src/Controller/AccountingPackageController.php b/hesabixCore/src/Controller/AccountingPackageController.php new file mode 100644 index 0000000..fb7bf09 --- /dev/null +++ b/hesabixCore/src/Controller/AccountingPackageController.php @@ -0,0 +1,167 @@ +entityManager = $entityManager; + $this->payMGR = $payMGR; + $this->registryMGR = $registryMGR; + } + + #[Route('/api/packagemanager/packages/list', name: 'list_accounting_packages', methods: ['POST'])] + public function listPackages(Access $access): JsonResponse + { + $acc = $access->hasRole('owner'); + if (!$acc) { + return $this->json(['result' => false, 'message' => 'به این بخش دسترسی ندارید'], 400); + } + $basePrice = (int) $this->registryMGR->get('system_settings', 'unlimited_price') ?: 500000; + $selectedDurations = json_decode($this->registryMGR->get('system_settings', 'unlimited_duration') ?: '[]', true); + + $packages = array_map(function ($month) use ($basePrice) { + return [ + 'month' => (int) $month, + 'price' => (string) ((int) $month * $basePrice), + ]; + }, $selectedDurations); + + // مرتب‌سازی بر اساس ماه + usort($packages, fn($a, $b) => $a['month'] <=> $b['month']); + + return $this->json(['packages' => $packages]); + } + + #[Route('/api/packagemanager/package/order/new', name: 'new_accounting_package_order', methods: ['POST'])] + public function newPackageOrder(Access $access, Request $request): JsonResponse + { + $acc = $access->hasRole('owner'); + $data = json_decode($request->getContent(), true); + $month = $data['month'] ?? null; + + if (!$month || !$acc) { + return $this->json(['result' => false, 'message' => 'اطلاعات ناقص است.'], 400); + } + + $selectedDurations = json_decode($this->registryMGR->get('system_settings', 'unlimited_duration') ?: '[]', true); + if (!in_array((string) $month, $selectedDurations)) { + return $this->json(['result' => false, 'message' => 'مدت زمان انتخاب‌شده مجاز نیست.'], 400); + } + + + $basePrice = (int) $this->registryMGR->get('system_settings', 'unlimited_price') ?: 500000; + $price = (string) ($month * $basePrice); + + $order = new AccountingPackageOrder(); + $order->setBid($acc['bid']); + $order->setDateSubmit((string) time()); + $order->setDateExpire((string) (time() + $month * 30 * 24 * 60 * 60)); + $order->setMonth($month); + $order->setPrice($price); + $order->setSubmitter($this->getUser()); + $order->setStatus(0); + $order->setDes("سفارش بسته حسابداری نامحدود $month ماهه"); + $this->entityManager->persist($order); + $this->entityManager->flush(); + $callbackUrl = $this->generateUrl('api_packagemanager_buy_verify', ['id' => $order->getId()], UrlGeneratorInterface::ABSOLUTE_URL); + $paymentResult = $this->payMGR->createRequest($price, $callbackUrl, $order->getDes(), $order->getId()); + $order->setGatePay($paymentResult['gate']); + $order->setVerifyCode($paymentResult['authkey']); + $this->entityManager->persist($order); + $this->entityManager->flush(); + + if ($paymentResult['Success']) { + $order->setGatePay($paymentResult['gate']); + $this->entityManager->persist($order); + $this->entityManager->flush(); + + return $this->json([ + 'result' => true, + 'paymentUrl' => $paymentResult['targetURL'], + 'message' => 'سفارش ثبت شد. به صفحه پرداخت هدایت می‌شوید.', + ]); + } + + return $this->json([ + 'result' => false, + 'message' => 'خطا در اتصال به درگاه پرداخت.', + ], 500); + } + + #[Route('/api/packagemanager/package/buy/verify/{id}', name: 'api_packagemanager_buy_verify')] + public function api_packagemanager_buy_verify(string $id, PayMGR $payMGR, Notification $notification, Request $request, EntityManagerInterface $entityManager, Log $log): Response + { + $req = $entityManager->getRepository(AccountingPackageOrder::class)->find($id); + if (!$req) + throw $this->createNotFoundException(''); + + $res = $payMGR->verify($req->getPrice(), $req->getVerifyCode(), $request); + if ($res['Success'] == false) { + $log->insert('بسته حسابداری نامحدود', 'پرداخت ناموفق بسته حسابداری نامحدود', $this->getUser(), $req->getBid()); + return $this->render('buy/fail.html.twig', ['results' => $res]); + } else { + $req->setStatus(100); + $req->setRefID($res['refID']); + $req->setCardPan($res['card_pan']); + $entityManager->persist($req); + $entityManager->flush(); + $log->insert( + 'بسته نامحدود حسابداری', + 'پرداخت موفق فاکتور بسته نامحدود حسابداری', + $req->getSubmitter(), + $req->getBid() + ); + $notification->insert('فاکتور بسته حسابداری نامحدود پرداخت شد.', '/acc/package/order/list', $req->getBid(), $req->getSubmitter()); + return $this->render('buy/success.html.twig', ['req' => $req]); + } + } + + #[Route('/api/packagemanager/packages/orders/list', name: 'list_accounting_package_orders', methods: ['POST'])] + public function listPackageOrders(Access $access): JsonResponse + { + $acc = $access->hasRole('owner'); + if (!$acc) { + return $this->json(['result' => false, 'message' => 'به این بخش دسترسی ندارید'], 403); + } + + $repository = $this->entityManager->getRepository(AccountingPackageOrder::class); + $orders = $repository->findBy(['bid' => $acc['bid']], ['dateSubmit' => 'DESC']); + + $ordersData = array_map(function (AccountingPackageOrder $order) { + return [ + 'id' => $order->getId(), + 'dateSubmit' => $order->getDateSubmit(), + 'dateExpire' => $order->getDateExpire(), + 'month' => $order->getMonth(), + 'price' => $order->getPrice(), + 'status' => $order->getStatus(), + 'des' => $order->getDes(), + ]; + }, $orders); + + return $this->json([ + 'result' => true, + 'orders' => $ordersData + ]); + } +} \ No newline at end of file diff --git a/hesabixCore/src/Controller/ArchiveController.php b/hesabixCore/src/Controller/ArchiveController.php index 52b0784..a45d78f 100644 --- a/hesabixCore/src/Controller/ArchiveController.php +++ b/hesabixCore/src/Controller/ArchiveController.php @@ -4,17 +4,16 @@ namespace App\Controller; use App\Entity\ArchiveFile; use App\Entity\ArchiveOrders; -use App\Entity\Settings; use App\Service\Access; use App\Service\Jdate; use App\Service\Log; use App\Service\Notification; use App\Service\PayMGR; use App\Service\Provider; +use App\Service\registryMGR; // اضافه کردن سرویس رجیستری use App\Service\twigFunctions; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\JsonResponse; @@ -47,6 +46,7 @@ class ArchiveController extends AbstractController 'used' => $usedSize ]; } + #[Route('/api/archive/info', name: 'app_archive_info')] public function app_archive_info(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse { @@ -58,51 +58,69 @@ class ArchiveController extends AbstractController } #[Route('/api/archive/order/settings', name: 'app_archive_order_settings')] - public function app_archive_order_settings(twigFunctions $functions, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse + public function app_archive_order_settings(registryMGR $registryMGR, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse { $acc = $access->hasRole('join'); if (!$acc) throw $this->createAccessDeniedException(); - $settings = $functions->systemSettings(); + + $rootSystem = 'system_settings'; + $storagePrice = (int) $registryMGR->get($rootSystem, 'cloud_price_per_gb'); // گرفتن قیمت از رجیستری + return $this->json([ - 'priceBase' => $settings->getStoragePrice() + 'priceBase' => $storagePrice ]); } #[Route('/api/archive/order/submit', name: 'app_archive_order_submit')] - public function app_archive_order_submit(PayMGR $payMGR, twigFunctions $functions, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse + public function app_archive_order_submit(PayMGR $payMGR, registryMGR $registryMGR, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse { $acc = $access->hasRole('join'); if (!$acc) throw $this->createAccessDeniedException(); + $params = []; if ($content = $request->getContent()) { $params = json_decode($content, true); } - $settings = $functions->systemSettings(); + + $rootSystem = 'system_settings'; + $storagePrice = (int) $registryMGR->get($rootSystem, 'cloud_price_per_gb'); // گرفتن قیمت از رجیستری + $order = new ArchiveOrders(); $order->setBid($acc['bid']); $order->setSubmitter($this->getUser()); $order->setDateSubmit(time()); - $order->setPrice($params['space'] * $params['month'] * $settings->getStoragePrice()); + $order->setPrice($params['space'] * $params['month'] * $storagePrice); // استفاده از قیمت رجیستری $order->setDes('خرید سرویس فضای ابری به مقدار ' . $params['space'] . ' گیگابایت به مدت ' . $params['month'] . ' ماه '); $order->setOrderSize($params['space']); $order->setMonth($params['month']); $entityManager->persist($order); $entityManager->flush(); - $result = $payMGR->createRequest($order->getPrice(), $this->generateUrl('api_archive_buy_verify', ["id"=>$order->getId()], UrlGeneratorInterface::ABSOLUTE_URL), 'خرید فضای ابری'); + + $result = $payMGR->createRequest( + $order->getPrice(), + $this->generateUrl('api_archive_buy_verify', ["id" => $order->getId()], UrlGeneratorInterface::ABSOLUTE_URL), + 'خرید فضای ابری' + ); + if ($result['Success']) { $order->setGatePay($result['gate']); $order->setVerifyCode($result['authkey']); $entityManager->persist($order); $entityManager->flush(); - $log->insert('سرویس فضای ابری', 'صدور فاکتور سرویس فضای ابری به مقدار ' . $params['space'] . ' گیگابایت به مدت ' . $params['month'] . ' ماه ', $this->getUser(), $acc['bid']); + $log->insert( + 'سرویس فضای ابری', + 'صدور فاکتور سرویس فضای ابری به مقدار ' . $params['space'] . ' گیگابایت به مدت ' . $params['month'] . ' ماه ', + $this->getUser(), + $acc['bid'] + ); } return $this->json($result); } #[Route('/api/archive/buy/verify/{id}', name: 'api_archive_buy_verify')] - public function api_archive_buy_verify(string $id, PayMGR $payMGR, twigFunctions $functions, Notification $notification, Request $request, EntityManagerInterface $entityManager, Log $log): Response + public function api_archive_buy_verify(string $id, PayMGR $payMGR, Notification $notification, Request $request, EntityManagerInterface $entityManager, Log $log): Response { $req = $entityManager->getRepository(ArchiveOrders::class)->find($id); if (!$req) @@ -125,7 +143,7 @@ class ArchiveController extends AbstractController $req->getSubmitter(), $req->getBid() ); - $notification->insert(' فاکتور فضای ابری پرداخت شد.', '/acc/sms/panel', $req->getBid(), $req->getSubmitter()); + $notification->insert('فاکتور فضای ابری پرداخت شد.', '/acc/sms/panel', $req->getBid(), $req->getSubmitter()); return $this->render('buy/success.html.twig', ['req' => $req]); } } @@ -187,27 +205,22 @@ class ArchiveController extends AbstractController $uploadedFile = $request->files->get('image'); if ($uploadedFile) { $originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME); - // this is needed to safely include the file name as part of the URL $safeFilename = $slugger->slug($originalFilename); $newFilename = $safeFilename . '-' . uniqid() . '.' . $uploadedFile->guessExtension(); - // Move the file to the directory where brochures are stored try { $uploadedFile->move( $this->getParameter('archiveTempMediaDir'), $newFilename ); } catch (FileException $e) { - // ... handle exception if something happens during file upload return $this->json("error"); } - // updates the 'brochureFilename' property to store the PDF file name - // instead of its contents - //$product->setBrochureFilename($newFilename); return $this->json(['name' => $newFilename]); } } + #[Route('/api/archive/file/save', name: 'app_archive_file_save')] public function app_archive_file_save(Jdate $jdate, Provider $provider, SluggerInterface $slugger, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, $code = 0): JsonResponse { @@ -228,7 +241,6 @@ class ArchiveController extends AbstractController $file->setFilename($item); $file->setDes($request->get('des')); $file->setCat($request->get('cat')); - //set file type $mimFile = mime_content_type(__DIR__ . '/../../../hesabixArchive/temp/' . $item); $file->setFileType($mimFile); $file->setFileSize(ceil(filesize(__DIR__ . '/../../../hesabixArchive/temp/' . $item) / (1024 * 1024))); @@ -238,14 +250,11 @@ class ArchiveController extends AbstractController $entityManager->persist($file); $entityManager->flush(); $log->insert('آرشیو', 'فایل با نام ' . $file->getFilename() . ' افزوده شد.', $this->getUser(), $acc['bid']); - } - } return $this->json([ 'ok' => 'ok' ]); - } #[Route('/api/archive/files/list', name: 'app_archive_file_list')] @@ -263,7 +272,6 @@ class ArchiveController extends AbstractController 'relatedDocType' => $params['type'], 'relatedDocCode' => $params['id'] ]); - echo $request->get('type'); $resp = []; foreach ($files as $file) { $temp = []; @@ -294,8 +302,8 @@ class ArchiveController extends AbstractController $fileAdr = __DIR__ . '/../../../hesabixArchive/' . $file->getFilename(); $response = new BinaryFileResponse($fileAdr); return $response; - } + #[Route('/api/archive/file/remove/{id}', name: 'app_archive_file_remove')] public function app_archive_file_remove(string $id, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse { @@ -314,4 +322,4 @@ class ArchiveController extends AbstractController $log->insert('آرشیو', 'فایل با نام ' . $file->getFilename() . ' حذف شد.', $this->getUser(), $acc['bid']); return $this->json(['result' => 1]); } -} +} \ No newline at end of file diff --git a/hesabixCore/src/Controller/BusinessController.php b/hesabixCore/src/Controller/BusinessController.php index 2258e6d..4917388 100644 --- a/hesabixCore/src/Controller/BusinessController.php +++ b/hesabixCore/src/Controller/BusinessController.php @@ -21,6 +21,7 @@ use App\Service\Extractor; use App\Service\Jdate; use App\Service\Log; use App\Service\Provider; +use App\Service\registryMGR; use Doctrine\ORM\EntityManagerInterface; use ReflectionException; @@ -114,7 +115,7 @@ class BusinessController extends AbstractController } #[Route('/api/business/insert', name: 'api_bussiness_insert')] - public function api_bussiness_insert(Jdate $jdate, Access $access, Log $log, Request $request, EntityManagerInterface $entityManager): Response + public function api_bussiness_insert(Jdate $jdate, Access $access, Log $log, Request $request, EntityManagerInterface $entityManager,registryMGR $registryMGR): Response { $params = []; if ($content = $request->getContent()) { @@ -262,6 +263,8 @@ class BusinessController extends AbstractController $entityManager->flush(); if ($isNew) { $perms = new Permission(); + $giftCredit = (int) $registryMGR->get('system_settings', 'gift_credit', 0); + $business->setSmsCharge($giftCredit); $perms->setBid($business); $perms->setUser($this->getUser()); $perms->setOwner(true); diff --git a/hesabixCore/src/Controller/CaptchaController.php b/hesabixCore/src/Controller/CaptchaController.php index ae942f9..7c1a106 100644 --- a/hesabixCore/src/Controller/CaptchaController.php +++ b/hesabixCore/src/Controller/CaptchaController.php @@ -11,8 +11,8 @@ use App\Service\CaptchaService; class CaptchaController extends AbstractController { #[Route('/api/captcha/image', name: 'api_captcha_image', methods: ['GET'])] - public function generateCaptchaImage(SessionInterface $session,CaptchaService $captchaService): Response + public function generateCaptchaImage(CaptchaService $captchaService): Response { - return $captchaService->createCaptchaImage($session); + return $captchaService->createCaptchaImage(); } } \ No newline at end of file diff --git a/hesabixCore/src/Controller/System/RegistrySettingsController.php b/hesabixCore/src/Controller/System/RegistrySettingsController.php new file mode 100644 index 0000000..650bcd3 --- /dev/null +++ b/hesabixCore/src/Controller/System/RegistrySettingsController.php @@ -0,0 +1,84 @@ +registryMGR = $registryMGR; + } + + #[Route('/api/settings/get/can-free-accounting', name: 'get_can_free_accounting', methods: ['POST'])] + public function getCanFreeAccounting(Request $request): JsonResponse + { + try { + $value = $this->registryMGR->get('system_settings', 'can_free_accounting'); + $responseValue = ($value === '1' || $value === 1) ? 1 : 0; + return $this->json(['value' => $responseValue]); + } catch (\Exception $e) { + return $this->json([ + 'value' => 0, + 'error' => 'خطا در بررسی تنظیمات: ' . $e->getMessage() + ], 500); + } + } + + #[Route('/api/admin/registry/settings/load', name: 'app_registry_settings_load', methods: ['POST'])] + public function app_registry_settings_load(EntityManagerInterface $entityManagerInterface, registryMGR $registryMGR): JsonResponse + { + $rootSystem = 'system_settings'; + $rootTicket = 'ticket'; + + $settings = [ + 'canRegister' => filter_var($registryMGR->get($rootSystem, 'can_register'), FILTER_VALIDATE_BOOLEAN), + 'canFreeAccounting' => filter_var($registryMGR->get($rootSystem, 'can_free_accounting'), FILTER_VALIDATE_BOOLEAN), + 'smsPrice' => (int) $registryMGR->get($rootSystem, 'sms_price'), + 'cloudPricePerGb' => (int) $registryMGR->get($rootSystem, 'cloud_price_per_gb'), + 'unlimitedPrice' => (int) $registryMGR->get($rootSystem, 'unlimited_price'), + 'accountingDocPrice' => (int) $registryMGR->get($rootSystem, 'accounting_doc_price'), + 'giftCredit' => (int) $registryMGR->get($rootSystem, 'gift_credit', 0), // مقدار پیش‌فرض 0 + 'unlimitedDuration' => json_decode($registryMGR->get($rootSystem, 'unlimited_duration') ?: '[]', true), + 'smsAlertEnabled' => filter_var($registryMGR->get($rootSystem, 'sms_alert_enabled'), FILTER_VALIDATE_BOOLEAN), + 'smsAlertMobile' => $registryMGR->get($rootTicket, 'managerMobile'), + ]; + + return new JsonResponse([ + 'result' => 1, + 'data' => $settings + ]); + } + + #[Route('/api/admin/registry/settings/save', name: 'app_registry_settings_save', methods: ['POST'])] + public function app_registry_settings_save(Request $request, EntityManagerInterface $entityManagerInterface, registryMGR $registryMGR): JsonResponse + { + $data = json_decode($request->getContent(), true); + $rootSystem = 'system_settings'; + $rootTicket = 'ticket'; + + $registryMGR->update($rootSystem, 'can_register', $data['canRegister'] ? '1' : '0'); + $registryMGR->update($rootSystem, 'can_free_accounting', $data['canFreeAccounting'] ? '1' : '0'); + $registryMGR->update($rootSystem, 'sms_price', (string) $data['smsPrice']); + $registryMGR->update($rootSystem, 'cloud_price_per_gb', (string) $data['cloudPricePerGb']); + $registryMGR->update($rootSystem, 'unlimited_price', (string) $data['unlimitedPrice']); + $registryMGR->update($rootSystem, 'accounting_doc_price', (string) $data['accountingDocPrice']); + $registryMGR->update($rootSystem, 'gift_credit', (string) $data['giftCredit']); // ذخیره فیلد جدید + $registryMGR->update($rootSystem, 'unlimited_duration', json_encode($data['unlimitedDuration'])); + $registryMGR->update($rootSystem, 'sms_alert_enabled', $data['smsAlertEnabled'] ? '1' : '0'); + $registryMGR->update($rootTicket, 'managerMobile', $data['smsAlertMobile'] ?? ''); + + return new JsonResponse([ + 'result' => 1, + 'message' => 'Settings saved successfully' + ]); + } +} \ No newline at end of file diff --git a/hesabixCore/src/Controller/UserController.php b/hesabixCore/src/Controller/UserController.php index a9237ce..3be3222 100644 --- a/hesabixCore/src/Controller/UserController.php +++ b/hesabixCore/src/Controller/UserController.php @@ -704,4 +704,16 @@ class UserController extends AbstractController 'id' => $user->getId(), ])); } + + #[Route('/api/user/check-register-status', name: 'app_check_register_status', methods: ['GET'])] + public function checkRegisterStatus(registryMGR $registryMGR): JsonResponse + { + $rootSystem = 'system_settings'; + $canRegister = filter_var($registryMGR->get($rootSystem, 'can_register'), FILTER_VALIDATE_BOOLEAN); + + return new JsonResponse([ + 'result' => 1, + 'canRegister' => $canRegister + ]); + } } diff --git a/hesabixCore/src/Entity/AccountingPackageOrder.php b/hesabixCore/src/Entity/AccountingPackageOrder.php new file mode 100644 index 0000000..e454226 --- /dev/null +++ b/hesabixCore/src/Entity/AccountingPackageOrder.php @@ -0,0 +1,203 @@ +id; + } + + public function getBid(): ?Business + { + return $this->bid; + } + + public function setBid(?Business $bid): static + { + $this->bid = $bid; + + return $this; + } + + public function getDateSubmit(): ?string + { + return $this->dateSubmit; + } + + public function setDateSubmit(string $dateSubmit): static + { + $this->dateSubmit = $dateSubmit; + + return $this; + } + + public function getDateExpire(): ?string + { + return $this->dateExpire; + } + + public function setDateExpire(string $dateExpire): static + { + $this->dateExpire = $dateExpire; + + return $this; + } + + public function getMonth(): ?int + { + return $this->month; + } + + public function setMonth(int $month): static + { + $this->month = $month; + + return $this; + } + + public function getPrice(): ?string + { + return $this->price; + } + + public function setPrice(string $price): static + { + $this->price = $price; + + return $this; + } + + public function getDes(): ?string + { + return $this->des; + } + + public function setDes(?string $des): static + { + $this->des = $des; + + return $this; + } + + public function getSubmitter(): ?User + { + return $this->submitter; + } + + public function setSubmitter(?User $submitter): static + { + $this->submitter = $submitter; + + return $this; + } + + public function getGatePay(): ?string + { + return $this->gatePay; + } + + public function setGatePay(?string $gatePay): static + { + $this->gatePay = $gatePay; + + return $this; + } + + public function getStatus(): ?int + { + return $this->status; + } + + public function setStatus(?int $status): static + { + $this->status = $status; + + return $this; + } + + public function getRefID(): ?string + { + return $this->refID; + } + + public function setRefID(?string $refID): static + { + $this->refID = $refID; + + return $this; + } + + public function getCardPan(): ?string + { + return $this->cardPan; + } + + public function setCardPan(?string $cardPan): static + { + $this->cardPan = $cardPan; + + return $this; + } + + public function getVerifyCode(): ?string + { + return $this->verifyCode; + } + + public function setVerifyCode(?string $verifyCode): static + { + $this->verifyCode = $verifyCode; + + return $this; + } +} diff --git a/hesabixCore/src/Entity/Business.php b/hesabixCore/src/Entity/Business.php index 00d7b29..87e0c2a 100644 --- a/hesabixCore/src/Entity/Business.php +++ b/hesabixCore/src/Entity/Business.php @@ -285,6 +285,12 @@ class Business #[ORM\Column(length: 255, nullable: true)] private ?string $sealFile = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'bid', targetEntity: AccountingPackageOrder::class, orphanRemoval: true)] + private Collection $accountingPackageOrders; + public function __construct() { $this->logs = new ArrayCollection(); @@ -325,6 +331,7 @@ class Business $this->preInvoiceItems = new ArrayCollection(); $this->dashboardSettings = new ArrayCollection(); $this->hesabdariTables = new ArrayCollection(); + $this->accountingPackageOrders = new ArrayCollection(); } public function getId(): ?int @@ -1981,4 +1988,34 @@ class Business return $this; } + + /** + * @return Collection + */ + public function getAccountingPackageOrders(): Collection + { + return $this->accountingPackageOrders; + } + + public function addAccountingPackageOrder(AccountingPackageOrder $accountingPackageOrder): static + { + if (!$this->accountingPackageOrders->contains($accountingPackageOrder)) { + $this->accountingPackageOrders->add($accountingPackageOrder); + $accountingPackageOrder->setBid($this); + } + + return $this; + } + + public function removeAccountingPackageOrder(AccountingPackageOrder $accountingPackageOrder): static + { + if ($this->accountingPackageOrders->removeElement($accountingPackageOrder)) { + // set the owning side to null (unless already changed) + if ($accountingPackageOrder->getBid() === $this) { + $accountingPackageOrder->setBid(null); + } + } + + return $this; + } } diff --git a/hesabixCore/src/Entity/Settings.php b/hesabixCore/src/Entity/Settings.php index 7c6e113..cbdf5cd 100644 --- a/hesabixCore/src/Entity/Settings.php +++ b/hesabixCore/src/Entity/Settings.php @@ -21,9 +21,6 @@ class Settings #[ORM\Column(length: 255, nullable: true)] private ?string $appSite = null; - #[ORM\Column(length: 255, nullable: true)] - private ?string $storagePrice = null; - #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $siteKeywords = null; @@ -39,9 +36,6 @@ class Settings #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $footer = null; - #[ORM\Column(length: 50, nullable: true)] - private ?string $activeSmsPanel = null; - public function getId(): ?int { return $this->id; @@ -71,18 +65,6 @@ class Settings return $this; } - public function getStoragePrice(): ?string - { - return $this->storagePrice; - } - - public function setStoragePrice(?string $storagePrice): static - { - $this->storagePrice = $storagePrice; - - return $this; - } - public function getSiteKeywords(): ?string { return $this->siteKeywords; @@ -142,16 +124,4 @@ class Settings return $this; } - - public function getActiveSmsPanel(): ?string - { - return $this->activeSmsPanel; - } - - public function setActiveSmsPanel(?string $activeSmsPanel): static - { - $this->activeSmsPanel = $activeSmsPanel; - - return $this; - } } diff --git a/hesabixCore/src/Entity/User.php b/hesabixCore/src/Entity/User.php index 9464eaa..cf5a760 100644 --- a/hesabixCore/src/Entity/User.php +++ b/hesabixCore/src/Entity/User.php @@ -119,6 +119,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\ManyToOne(targetEntity: self::class)] private ?self $invitedBy = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'submitter', targetEntity: AccountingPackageOrder::class, orphanRemoval: true)] + private Collection $accountingPackageOrders; + public function __construct() { $this->userTokens = new ArrayCollection(); @@ -141,6 +147,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface $this->notes = new ArrayCollection(); $this->preInvoiceDocs = new ArrayCollection(); $this->dashboardSettings = new ArrayCollection(); + $this->accountingPackageOrders = new ArrayCollection(); } public function getId(): ?int @@ -886,4 +893,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + + /** + * @return Collection + */ + public function getAccountingPackageOrders(): Collection + { + return $this->accountingPackageOrders; + } + + public function addAccountingPackageOrder(AccountingPackageOrder $accountingPackageOrder): static + { + if (!$this->accountingPackageOrders->contains($accountingPackageOrder)) { + $this->accountingPackageOrders->add($accountingPackageOrder); + $accountingPackageOrder->setSubmitter($this); + } + + return $this; + } + + public function removeAccountingPackageOrder(AccountingPackageOrder $accountingPackageOrder): static + { + if ($this->accountingPackageOrders->removeElement($accountingPackageOrder)) { + // set the owning side to null (unless already changed) + if ($accountingPackageOrder->getSubmitter() === $this) { + $accountingPackageOrder->setSubmitter(null); + } + } + + return $this; + } } diff --git a/hesabixCore/src/Repository/AccountingPackageOrderRepository.php b/hesabixCore/src/Repository/AccountingPackageOrderRepository.php new file mode 100644 index 0000000..c12ab9e --- /dev/null +++ b/hesabixCore/src/Repository/AccountingPackageOrderRepository.php @@ -0,0 +1,43 @@ + + */ +class AccountingPackageOrderRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, AccountingPackageOrder::class); + } + + // /** + // * @return AccountingPackageOrder[] Returns an array of AccountingPackageOrder objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('a') + // ->andWhere('a.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('a.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?AccountingPackageOrder + // { + // return $this->createQueryBuilder('a') + // ->andWhere('a.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/hesabixCore/src/Service/AccountingPermissionService.php b/hesabixCore/src/Service/AccountingPermissionService.php new file mode 100644 index 0000000..0831941 --- /dev/null +++ b/hesabixCore/src/Service/AccountingPermissionService.php @@ -0,0 +1,86 @@ +entityManager = $entityManager; + $this->registryMGR = $registryMGR; + } + + private function getAccountingDocPrice(): int + { + $rootSystem = 'system_settings'; + return (int) $this->registryMGR->get($rootSystem, 'accountingDocPrice'); + } + + /** + * چک می‌کنه که آیا کسب‌وکار می‌تونه سند حسابداری ثبت کنه یا نه + * + * @param Business $business + * @return array{result: bool, message: string, code: int} + */ + public function canRegisterAccountingDoc(Business $business): array + { + $rootSystem = 'system_settings'; + + // ۱. چک کردن ثبت رایگان سند حسابداری + if ($this->registryMGR->get($rootSystem, 'canFreeAccounting') === true) { + return [ + 'result' => true, + 'message' => 'ثبت سند حسابداری به صورت رایگان مجاز است.', + 'code' => 1 + ]; + } + + // ۲. چک کردن پکیج حسابداری فعال (با وضعیت پرداخت‌شده) + $currentTime = time(); + $packageOrders = $this->entityManager->getRepository(AccountingPackageOrder::class)->findBy([ + 'bid' => $business->getId(), + 'status' => 100 // فقط پکیج‌های پرداخت‌شده + ]); + + foreach ($packageOrders as $order) { + if ((int) $order->getDateExpire() > $currentTime) { + return [ + 'result' => true, + 'message' => 'کسب‌وکار دارای پکیج فعال حسابداری است.', + 'code' => 2 + ]; + } + } + + // ۳. چک کردن اعتبار موجود (smsCharge) و کسر هزینه + $accountingDocPrice = $this->getAccountingDocPrice(); + $smsCharge = (int) $business->getSmsCharge(); + + if ($smsCharge >= $accountingDocPrice) { + // کسر هزینه از اعتبار + $business->setSmsCharge((string) ($smsCharge - $accountingDocPrice)); + $this->entityManager->persist($business); + $this->entityManager->flush(); + + return [ + 'result' => true, + 'message' => 'هزینه سند حسابداری از اعتبار کسر شد.', + 'code' => 3 + ]; + } + + // ۴. اگه هیچ‌کدوم از شرط‌ها برقرار نبود + return [ + 'result' => false, + 'message' => 'اعتبار کافی برای ثبت سند حسابداری وجود ندارد. لطفاً اعتبار خود را افزایش دهید یا پکیج حسابداری خریداری کنید.', + 'code' => 4 + ]; + } +} \ No newline at end of file diff --git a/hesabixCore/src/Service/CaptchaService.php b/hesabixCore/src/Service/CaptchaService.php index 146d53e..6a20a8e 100644 --- a/hesabixCore/src/Service/CaptchaService.php +++ b/hesabixCore/src/Service/CaptchaService.php @@ -72,7 +72,10 @@ class CaptchaService imagedestroy($image); imagedestroy($wavedImage); - return new Response($imageData, 200, ['Content-Type' => 'image/png']); + $response = new Response($imageData, 200, ['Content-Type' => 'image/png']); + $response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0'); + $response->headers->set('Pragma', 'no-cache'); + $response->headers->set('Expires', '0'); } public function isCaptchaRequired(string $attemptKey): bool diff --git a/hesabixCore/src/Service/SMS.php b/hesabixCore/src/Service/SMS.php index 94b12c0..2011706 100644 --- a/hesabixCore/src/Service/SMS.php +++ b/hesabixCore/src/Service/SMS.php @@ -1,5 +1,6 @@ entityManager = $entityManager; $this->registryMGR = $registryMGR; $this->settings = $entityManager->getRepository(Settings::class)->findAll()[0]; + } + public function getSmsPrice(): int + { + $rootSystem = 'system_settings'; + return (int) $this->registryMGR->get($rootSystem, 'sms_price'); // گرفتن قیمت از رجیستری } public function send(array $params, $bodyID, $to): void @@ -34,18 +38,15 @@ class SMS $password = $this->registryMGR->get('sms', 'password'); $api = new MelipayamakApi($username, $password); $sms = $api->sms('soap'); - $response = $sms->sendByBaseNumber($params, $to, $bodyID); + $response = $sms->sendByBalanceNumber($params, $to, $bodyID); $json = json_decode($response); - } catch (\Exception $e) { echo $e->getMessage(); die(); } - } elseif ($this->registryMGR->get('sms', 'plan') == 'idepayam') { ini_set("soap.wsdl_cache_enabled", "0"); - //create next $pt = []; foreach ($params as $param) { $pt['{' . strval(array_search($param, $params)) . '}'] = $param; @@ -58,14 +59,13 @@ class SMS $soap->Content = json_encode($pt, JSON_UNESCAPED_UNICODE); $soap->Type = 0; $array = $soap->SendSMSByPattern($soap->fromNum, $soap->toNum, $soap->Content, $soap->patternID, $soap->Type, $soap->token); - } elseif ($this->registryMGR->get('sms', 'plan') == 'ippanel') { $toArray = [$to]; $username = $this->registryMGR->get('sms', 'username'); $password = $this->registryMGR->get('sms', 'password'); $from = $this->registryMGR->get('sms', 'fromNum'); $input_data = []; - foreach ($params as $key=>$param) { + foreach ($params as $key => $param) { $input_data['p' . strval(array_search($param, $params))] = $param; } $url = "https://ippanel.com/patterns/pattern?username=" . $username . "&password=" . urlencode($password) . "&from=$from&to=" . json_encode($toArray) . "&input_data=" . urlencode(json_encode($input_data)) . "&pattern_code=$bodyID"; @@ -75,27 +75,30 @@ class SMS curl_setopt($handler, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($handler); } - } public function sendByBalance(array $params, $bodyID, $to, Business $business, User $user, $balance = 500): int { - if ($business->getSmsCharge() < ($balance * $this->smsPrice)) + $smsPrice = $this->getSmsPrice(); // گرفتن قیمت دینامیک از رجیستری + + if ($business->getSmsCharge() < ($balance * $smsPrice)) return 2; + $this->send($params, $bodyID, $to); - $business->setSmsCharge($business->getSmsCharge() - ($balance * $this->smsPrice)); + $business->setSmsCharge($business->getSmsCharge() - ($balance * $smsPrice)); $this->entityManager->persist($business); $this->entityManager->flush(); - //save logs + + // ثبت لاگ $log = new \App\Entity\Log(); $log->setBid($business); $log->setDateSubmit(time()); $log->setPart('پیامک'); $log->setUser($user); - $log->setDes('ارسال پیامک به طول ' . $balance . ' پیامک به شماره ' . $to . ' با شماره الگو ' . $bodyID . ' هزینه: ' . ($this->smsPrice * $balance) . ' ریال '); + $log->setDes('ارسال پیامک به طول ' . $balance . ' پیامک به شماره ' . $to . ' با شماره الگو ' . $bodyID . ' هزینه: ' . ($smsPrice * $balance) . ' ریال '); $this->entityManager->persist($log); $this->entityManager->flush(); + return 1; } - } \ No newline at end of file