feat: port payment management tab from tauri to admin dashboard
Add per-clinic payment methods (bank accounts + POS/card-reader devices)
under the "مدیریت پرداخت" settings tab at /admin/my-financial, ported from
clinic-pro-tauri's mock-only PaymentManagement tab into a real persisted
feature. These records are referenceable (by uuid) from patient invoices to
record which method a service payment was made with.
Backend (new src/PaymentMethod domain):
- BankAccount + Pos entities, repositories, PaymentMethodService (validation,
ownership scoping, create/update/toggle logic).
- Thin PaymentMethodController exposing /api/v1/my/payment-methods/{bank-accounts,pos}
(GET/POST/PUT + PATCH .../status), guarded to clinic/doctor/secretary/admin.
- Migration for bank_accounts + pos_devices tables.
- Functional tests (success + validation/404/403 + empty boundaries).
- docs/api/payment-method.md.
Frontend:
- Replace MyFinancialPage content with the payment-management UI (two tabs,
tables, add/edit modals, status toggle) using the admin design system.
- usePaymentMethods hook (TanStack Query) + presentational components.
- Update page test to cover tabs, data, empty state and the add modal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Service\PaymentMethodService;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
/**
|
||||
* Per-clinic payment methods: bank accounts and POS (card reader) devices.
|
||||
* Scoped to the acting user; only clinic/doctor/secretary roles may manage them.
|
||||
*/
|
||||
#[OA\Tag(name: 'Payment Methods')]
|
||||
#[Route('/api/v1/my/payment-methods')]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
class PaymentMethodController extends BaseController
|
||||
{
|
||||
private const ALLOWED_ROLES = ['ROLE_CLINIC', 'ROLE_DOCTOR', 'ROLE_SECRETARY', 'ROLE_ADMIN'];
|
||||
|
||||
public function __construct(
|
||||
private readonly PaymentMethodService $service,
|
||||
) {}
|
||||
|
||||
// ---- Bank accounts -----------------------------------------------------
|
||||
|
||||
#[Route('/bank-accounts', methods: ['GET'])]
|
||||
public function listBankAccounts(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
|
||||
return $this->success($this->service->listBankAccounts($user));
|
||||
}
|
||||
|
||||
#[Route('/bank-accounts', methods: ['POST'])]
|
||||
public function createBankAccount(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
return $this->success($this->service->createBankAccount($user, $data), 201);
|
||||
}
|
||||
|
||||
#[Route('/bank-accounts/{uuid}', methods: ['PUT'])]
|
||||
public function updateBankAccount(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
return $this->success($this->service->updateBankAccount($user, $uuid, $data));
|
||||
}
|
||||
|
||||
#[Route('/bank-accounts/{uuid}/status', methods: ['PATCH'])]
|
||||
public function toggleBankAccountStatus(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
|
||||
return $this->success($this->service->toggleBankAccountStatus($user, $uuid));
|
||||
}
|
||||
|
||||
// ---- POS devices -------------------------------------------------------
|
||||
|
||||
#[Route('/pos', methods: ['GET'])]
|
||||
public function listPos(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
|
||||
return $this->success($this->service->listPos($user));
|
||||
}
|
||||
|
||||
#[Route('/pos', methods: ['POST'])]
|
||||
public function createPos(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
return $this->success($this->service->createPos($user, $data), 201);
|
||||
}
|
||||
|
||||
#[Route('/pos/{uuid}', methods: ['PUT'])]
|
||||
public function updatePos(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
|
||||
return $this->success($this->service->updatePos($user, $uuid, $data));
|
||||
}
|
||||
|
||||
#[Route('/pos/{uuid}/status', methods: ['PATCH'])]
|
||||
public function togglePosStatus(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$this->assertRole($user);
|
||||
|
||||
return $this->success($this->service->togglePosStatus($user, $uuid));
|
||||
}
|
||||
|
||||
private function assertRole(User $user): void
|
||||
{
|
||||
if (!array_intersect(self::ALLOWED_ROLES, $user->getRoles())) {
|
||||
throw new \App\Shared\Exception\AppException(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی ندارید', 403);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Entity;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Repository\BankAccountRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* A clinic's bank account used as a payment method. Referenced from patient
|
||||
* invoices to record which account a service payment was made to. This entity
|
||||
* only stores the account info; the payment linkage lives on the invoice side.
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: BankAccountRepository::class)]
|
||||
#[ORM\Table(name: 'bank_accounts')]
|
||||
#[ORM\Index(columns: ['user_id'], name: 'idx_bank_accounts_user')]
|
||||
class BankAccount
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
||||
private string $uuid;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'RESTRICT')]
|
||||
private User $user;
|
||||
|
||||
#[ORM\Column(name: 'bank_name', type: 'string', length: 100)]
|
||||
private string $bankName;
|
||||
|
||||
#[ORM\Column(name: 'card_number', type: 'string', length: 32, nullable: true)]
|
||||
private ?string $cardNumber = null;
|
||||
|
||||
#[ORM\Column(name: 'account_number', type: 'string', length: 64)]
|
||||
private string $accountNumber;
|
||||
|
||||
#[ORM\Column(name: 'shaba_number', type: 'string', length: 34, nullable: true)]
|
||||
private ?string $shabaNumber = null;
|
||||
|
||||
#[ORM\Column(name: 'is_active', type: 'boolean')]
|
||||
private bool $isActive = true;
|
||||
|
||||
#[ORM\Column(name: 'created_at', type: 'integer')]
|
||||
private int $createdAt;
|
||||
|
||||
#[ORM\Column(name: 'updated_at', type: 'integer')]
|
||||
private int $updatedAt;
|
||||
|
||||
public function __construct(
|
||||
User $user,
|
||||
string $bankName,
|
||||
string $accountNumber,
|
||||
?string $cardNumber = null,
|
||||
?string $shabaNumber = null,
|
||||
) {
|
||||
$this->uuid = Uuid::v4()->toRfc4122();
|
||||
$this->user = $user;
|
||||
$this->bankName = $bankName;
|
||||
$this->accountNumber = $accountNumber;
|
||||
$this->cardNumber = $cardNumber ?: null;
|
||||
$this->shabaNumber = $shabaNumber ?: null;
|
||||
$this->createdAt = time();
|
||||
$this->updatedAt = time();
|
||||
}
|
||||
|
||||
public function getId(): ?int { return $this->id; }
|
||||
public function getUuid(): string { return $this->uuid; }
|
||||
public function getUser(): User { return $this->user; }
|
||||
public function getBankName(): string { return $this->bankName; }
|
||||
public function getCardNumber(): ?string { return $this->cardNumber; }
|
||||
public function getAccountNumber(): string { return $this->accountNumber; }
|
||||
public function getShabaNumber(): ?string { return $this->shabaNumber; }
|
||||
public function isActive(): bool { return $this->isActive; }
|
||||
|
||||
public function setBankName(string $v): self { $this->bankName = $v; $this->touch(); return $this; }
|
||||
public function setCardNumber(?string $v): self { $this->cardNumber = $v ?: null; $this->touch(); return $this; }
|
||||
public function setAccountNumber(string $v): self { $this->accountNumber = $v; $this->touch(); return $this; }
|
||||
public function setShabaNumber(?string $v): self { $this->shabaNumber = $v ?: null; $this->touch(); return $this; }
|
||||
public function setActive(bool $v): self { $this->isActive = $v; $this->touch(); return $this; }
|
||||
|
||||
private function touch(): void { $this->updatedAt = time(); }
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'bank_name' => $this->bankName,
|
||||
'card_number' => $this->cardNumber,
|
||||
'account_number' => $this->accountNumber,
|
||||
'shaba_number' => $this->shabaNumber,
|
||||
'is_active' => $this->isActive,
|
||||
'created_at' => $this->createdAt,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Entity;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Repository\PosRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
/**
|
||||
* A clinic's card reader (POS) device used as a payment method. Referenced from
|
||||
* patient invoices to record which device a service payment was collected on.
|
||||
*/
|
||||
#[ORM\Entity(repositoryClass: PosRepository::class)]
|
||||
#[ORM\Table(name: 'pos_devices')]
|
||||
#[ORM\Index(columns: ['user_id'], name: 'idx_pos_devices_user')]
|
||||
class Pos
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
||||
private string $uuid;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'RESTRICT')]
|
||||
private User $user;
|
||||
|
||||
#[ORM\Column(name: 'bank_name', type: 'string', length: 100)]
|
||||
private string $bankName;
|
||||
|
||||
#[ORM\Column(name: 'serial_number', type: 'string', length: 64, nullable: true)]
|
||||
private ?string $serialNumber = null;
|
||||
|
||||
#[ORM\Column(name: 'terminal_number', type: 'string', length: 64)]
|
||||
private string $terminalNumber;
|
||||
|
||||
#[ORM\Column(name: 'account_number', type: 'string', length: 64, nullable: true)]
|
||||
private ?string $accountNumber = null;
|
||||
|
||||
#[ORM\Column(name: 'is_active', type: 'boolean')]
|
||||
private bool $isActive = true;
|
||||
|
||||
#[ORM\Column(name: 'created_at', type: 'integer')]
|
||||
private int $createdAt;
|
||||
|
||||
#[ORM\Column(name: 'updated_at', type: 'integer')]
|
||||
private int $updatedAt;
|
||||
|
||||
public function __construct(
|
||||
User $user,
|
||||
string $bankName,
|
||||
string $terminalNumber,
|
||||
?string $serialNumber = null,
|
||||
?string $accountNumber = null,
|
||||
) {
|
||||
$this->uuid = Uuid::v4()->toRfc4122();
|
||||
$this->user = $user;
|
||||
$this->bankName = $bankName;
|
||||
$this->terminalNumber = $terminalNumber;
|
||||
$this->serialNumber = $serialNumber ?: null;
|
||||
$this->accountNumber = $accountNumber ?: null;
|
||||
$this->createdAt = time();
|
||||
$this->updatedAt = time();
|
||||
}
|
||||
|
||||
public function getId(): ?int { return $this->id; }
|
||||
public function getUuid(): string { return $this->uuid; }
|
||||
public function getUser(): User { return $this->user; }
|
||||
public function getBankName(): string { return $this->bankName; }
|
||||
public function getSerialNumber(): ?string { return $this->serialNumber; }
|
||||
public function getTerminalNumber(): string { return $this->terminalNumber; }
|
||||
public function getAccountNumber(): ?string { return $this->accountNumber; }
|
||||
public function isActive(): bool { return $this->isActive; }
|
||||
|
||||
public function setBankName(string $v): self { $this->bankName = $v; $this->touch(); return $this; }
|
||||
public function setSerialNumber(?string $v): self { $this->serialNumber = $v ?: null; $this->touch(); return $this; }
|
||||
public function setTerminalNumber(string $v): self { $this->terminalNumber = $v; $this->touch(); return $this; }
|
||||
public function setAccountNumber(?string $v): self { $this->accountNumber = $v ?: null; $this->touch(); return $this; }
|
||||
public function setActive(bool $v): self { $this->isActive = $v; $this->touch(); return $this; }
|
||||
|
||||
private function touch(): void { $this->updatedAt = time(); }
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'bank_name' => $this->bankName,
|
||||
'serial_number' => $this->serialNumber,
|
||||
'terminal_number' => $this->terminalNumber,
|
||||
'account_number' => $this->accountNumber,
|
||||
'is_active' => $this->isActive,
|
||||
'created_at' => $this->createdAt,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Repository;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Entity\BankAccount;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class BankAccountRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, BankAccount::class);
|
||||
}
|
||||
|
||||
public function findByUuid(string $uuid): ?BankAccount
|
||||
{
|
||||
return $this->findOneBy(['uuid' => $uuid]);
|
||||
}
|
||||
|
||||
/** @return BankAccount[] */
|
||||
public function findByUser(User $user): array
|
||||
{
|
||||
return $this->createQueryBuilder('b')
|
||||
->where('b.user = :user')->setParameter('user', $user)
|
||||
->orderBy('b.createdAt', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function save(BankAccount $entity, bool $flush = true): void
|
||||
{
|
||||
$this->getEntityManager()->persist($entity);
|
||||
if ($flush) $this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Repository;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Entity\Pos;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class PosRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Pos::class);
|
||||
}
|
||||
|
||||
public function findByUuid(string $uuid): ?Pos
|
||||
{
|
||||
return $this->findOneBy(['uuid' => $uuid]);
|
||||
}
|
||||
|
||||
/** @return Pos[] */
|
||||
public function findByUser(User $user): array
|
||||
{
|
||||
return $this->createQueryBuilder('p')
|
||||
->where('p.user = :user')->setParameter('user', $user)
|
||||
->orderBy('p.createdAt', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
public function save(Pos $entity, bool $flush = true): void
|
||||
{
|
||||
$this->getEntityManager()->persist($entity);
|
||||
if ($flush) $this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentMethod\Service;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\PaymentMethod\Entity\BankAccount;
|
||||
use App\PaymentMethod\Entity\Pos;
|
||||
use App\PaymentMethod\Repository\BankAccountRepository;
|
||||
use App\PaymentMethod\Repository\PosRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Exception\AppException;
|
||||
|
||||
/**
|
||||
* Business logic for a clinic's payment methods (bank accounts + POS devices).
|
||||
* Every read/write is scoped to the acting user so one clinic can never touch
|
||||
* another's records. Ported from clinic-pro-tauri PaymentManagement tab.
|
||||
*/
|
||||
class PaymentMethodService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BankAccountRepository $bankRepo,
|
||||
private readonly PosRepository $posRepo,
|
||||
) {}
|
||||
|
||||
// ---- Bank accounts -----------------------------------------------------
|
||||
|
||||
/** @return array<int, array<string, mixed>> */
|
||||
public function listBankAccounts(User $user): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (BankAccount $b) => $b->toArray(),
|
||||
$this->bankRepo->findByUser($user),
|
||||
);
|
||||
}
|
||||
|
||||
public function createBankAccount(User $user, array $data): array
|
||||
{
|
||||
$bankName = trim((string) ($data['bank_name'] ?? ''));
|
||||
$accountNumber = trim((string) ($data['account_number'] ?? ''));
|
||||
$cardNumber = trim((string) ($data['card_number'] ?? ''));
|
||||
$shabaNumber = trim((string) ($data['shaba_number'] ?? ''));
|
||||
|
||||
if ($bankName === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'نام بانک الزامی است', 422, 'bank_name');
|
||||
}
|
||||
if ($accountNumber === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'شماره حساب الزامی است', 422, 'account_number');
|
||||
}
|
||||
|
||||
$account = new BankAccount($user, $bankName, $accountNumber, $cardNumber, $shabaNumber);
|
||||
$this->bankRepo->save($account);
|
||||
|
||||
return $account->toArray();
|
||||
}
|
||||
|
||||
public function updateBankAccount(User $user, string $uuid, array $data): array
|
||||
{
|
||||
$account = $this->ownedBankAccount($user, $uuid);
|
||||
|
||||
if (array_key_exists('bank_name', $data)) {
|
||||
$bankName = trim((string) $data['bank_name']);
|
||||
if ($bankName === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'نام بانک الزامی است', 422, 'bank_name');
|
||||
}
|
||||
$account->setBankName($bankName);
|
||||
}
|
||||
if (array_key_exists('account_number', $data)) {
|
||||
$accountNumber = trim((string) $data['account_number']);
|
||||
if ($accountNumber === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'شماره حساب الزامی است', 422, 'account_number');
|
||||
}
|
||||
$account->setAccountNumber($accountNumber);
|
||||
}
|
||||
if (array_key_exists('card_number', $data)) {
|
||||
$account->setCardNumber(trim((string) $data['card_number']));
|
||||
}
|
||||
if (array_key_exists('shaba_number', $data)) {
|
||||
$account->setShabaNumber(trim((string) $data['shaba_number']));
|
||||
}
|
||||
|
||||
$this->bankRepo->save($account);
|
||||
|
||||
return $account->toArray();
|
||||
}
|
||||
|
||||
public function toggleBankAccountStatus(User $user, string $uuid): array
|
||||
{
|
||||
$account = $this->ownedBankAccount($user, $uuid);
|
||||
$account->setActive(!$account->isActive());
|
||||
$this->bankRepo->save($account);
|
||||
|
||||
return $account->toArray();
|
||||
}
|
||||
|
||||
private function ownedBankAccount(User $user, string $uuid): BankAccount
|
||||
{
|
||||
$account = $this->bankRepo->findByUuid($uuid);
|
||||
if ($account === null || $account->getUser()->getId() !== $user->getId()) {
|
||||
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'حساب بانکی یافت نشد', 404);
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
// ---- POS devices -------------------------------------------------------
|
||||
|
||||
/** @return array<int, array<string, mixed>> */
|
||||
public function listPos(User $user): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (Pos $p) => $p->toArray(),
|
||||
$this->posRepo->findByUser($user),
|
||||
);
|
||||
}
|
||||
|
||||
public function createPos(User $user, array $data): array
|
||||
{
|
||||
$bankName = trim((string) ($data['bank_name'] ?? ''));
|
||||
$terminalNumber = trim((string) ($data['terminal_number'] ?? ''));
|
||||
$serialNumber = trim((string) ($data['serial_number'] ?? ''));
|
||||
$accountNumber = trim((string) ($data['account_number'] ?? ''));
|
||||
|
||||
if ($bankName === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'نام بانک الزامی است', 422, 'bank_name');
|
||||
}
|
||||
if ($terminalNumber === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'شماره ترمینال الزامی است', 422, 'terminal_number');
|
||||
}
|
||||
|
||||
$pos = new Pos($user, $bankName, $terminalNumber, $serialNumber, $accountNumber);
|
||||
$this->posRepo->save($pos);
|
||||
|
||||
return $pos->toArray();
|
||||
}
|
||||
|
||||
public function updatePos(User $user, string $uuid, array $data): array
|
||||
{
|
||||
$pos = $this->ownedPos($user, $uuid);
|
||||
|
||||
if (array_key_exists('bank_name', $data)) {
|
||||
$bankName = trim((string) $data['bank_name']);
|
||||
if ($bankName === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'نام بانک الزامی است', 422, 'bank_name');
|
||||
}
|
||||
$pos->setBankName($bankName);
|
||||
}
|
||||
if (array_key_exists('terminal_number', $data)) {
|
||||
$terminalNumber = trim((string) $data['terminal_number']);
|
||||
if ($terminalNumber === '') {
|
||||
throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'شماره ترمینال الزامی است', 422, 'terminal_number');
|
||||
}
|
||||
$pos->setTerminalNumber($terminalNumber);
|
||||
}
|
||||
if (array_key_exists('serial_number', $data)) {
|
||||
$pos->setSerialNumber(trim((string) $data['serial_number']));
|
||||
}
|
||||
if (array_key_exists('account_number', $data)) {
|
||||
$pos->setAccountNumber(trim((string) $data['account_number']));
|
||||
}
|
||||
|
||||
$this->posRepo->save($pos);
|
||||
|
||||
return $pos->toArray();
|
||||
}
|
||||
|
||||
public function togglePosStatus(User $user, string $uuid): array
|
||||
{
|
||||
$pos = $this->ownedPos($user, $uuid);
|
||||
$pos->setActive(!$pos->isActive());
|
||||
$this->posRepo->save($pos);
|
||||
|
||||
return $pos->toArray();
|
||||
}
|
||||
|
||||
private function ownedPos(User $user, string $uuid): Pos
|
||||
{
|
||||
$pos = $this->posRepo->findByUuid($uuid);
|
||||
if ($pos === null || $pos->getUser()->getId() !== $user->getId()) {
|
||||
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'کارت خوان یافت نشد', 404);
|
||||
}
|
||||
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user