progress in add packages

This commit is contained in:
Hesabix 2025-03-12 18:27:05 +00:00
parent fb1a456e6a
commit 21a5c35b0a
14 changed files with 729 additions and 73 deletions

View file

@ -0,0 +1,167 @@
<?php
namespace App\Controller;
use App\Service\Access;
use App\Service\Log;
use App\Service\Notification;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\AccountingPackageOrder;
use App\Service\PayMGR;
use App\Service\registryMGR;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class AccountingPackageController extends AbstractController
{
private EntityManagerInterface $entityManager;
private PayMGR $payMGR;
private registryMGR $registryMGR;
public function __construct(EntityManagerInterface $entityManager, PayMGR $payMGR, registryMGR $registryMGR)
{
$this->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
]);
}
}

View file

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

View file

@ -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);

View file

@ -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();
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace App\Controller\System;
use App\Service\registryMGR;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
final class RegistrySettingsController extends AbstractController
{
private registryMGR $registryMGR;
public function __construct(registryMGR $registryMGR)
{
$this->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'
]);
}
}

View file

@ -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
]);
}
}

View file

@ -0,0 +1,203 @@
<?php
namespace App\Entity;
use App\Repository\AccountingPackageOrderRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: AccountingPackageOrderRepository::class)]
class AccountingPackageOrder
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'accountingPackageOrders')]
#[ORM\JoinColumn(nullable: false)]
private ?Business $bid = null;
#[ORM\Column(type: Types::BIGINT)]
private ?string $dateSubmit = null;
#[ORM\Column(type: Types::BIGINT)]
private ?string $dateExpire = null;
#[ORM\Column]
private ?int $month = null;
#[ORM\Column(type: Types::BIGINT)]
private ?string $price = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $des = null;
#[ORM\ManyToOne(inversedBy: 'accountingPackageOrders')]
#[ORM\JoinColumn(nullable: false)]
private ?User $submitter = null;
#[ORM\Column(length: 100, nullable: true)]
private ?string $gatePay = null;
#[ORM\Column(nullable: true)]
private ?int $status = null;
#[ORM\Column(length: 100, nullable: true)]
private ?string $refID = null;
#[ORM\Column(length: 60, nullable: true)]
private ?string $cardPan = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $verifyCode = null;
public function getId(): ?int
{
return $this->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;
}
}

View file

@ -285,6 +285,12 @@ class Business
#[ORM\Column(length: 255, nullable: true)]
private ?string $sealFile = null;
/**
* @var Collection<int, AccountingPackageOrder>
*/
#[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<int, AccountingPackageOrder>
*/
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;
}
}

View file

@ -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;
}
}

View file

@ -119,6 +119,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\ManyToOne(targetEntity: self::class)]
private ?self $invitedBy = null;
/**
* @var Collection<int, AccountingPackageOrder>
*/
#[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<int, AccountingPackageOrder>
*/
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;
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Repository;
use App\Entity\AccountingPackageOrder;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<AccountingPackageOrder>
*/
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()
// ;
// }
}

View file

@ -0,0 +1,86 @@
<?php
namespace App\Service;
use App\Entity\Business;
use App\Entity\AccountingPackageOrder;
use Doctrine\ORM\EntityManagerInterface;
class AccountingPermissionService
{
private EntityManagerInterface $entityManager;
private registryMGR $registryMGR;
public function __construct(EntityManagerInterface $entityManager, registryMGR $registryMGR)
{
$this->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
];
}
}

View file

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

View file

@ -1,5 +1,6 @@
<?php
namespace App\Service;
use App\Entity\Business;
use App\Entity\Registry;
use App\Entity\Settings;
@ -13,8 +14,6 @@ class SMS
private Settings $settings;
private registryMGR $registryMGR;
private int $smsPrice = 1500;
/**
* @param EntityManagerInterface $entityManager
*/
@ -23,7 +22,12 @@ class SMS
$this->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;
}
}