update for two-step system

This commit is contained in:
Gloomy 2025-08-19 14:16:17 +00:00
parent e775de8f77
commit f3517d55d6
6 changed files with 748 additions and 433 deletions

View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250819120657 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE hesabdari_row ADD is_preview TINYINT(1) DEFAULT NULL, ADD is_approved TINYINT(1) DEFAULT NULL, ADD approved_by_id INT DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE hesabdari_row ADD CONSTRAINT FK_83B2C6EC2D234F6A FOREIGN KEY (approved_by_id) REFERENCES user (id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_83B2C6EC2D234F6A ON hesabdari_row (approved_by_id)
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE hesabdari_row DROP FOREIGN KEY FK_83B2C6EC2D234F6A
SQL);
$this->addSql(<<<'SQL'
DROP INDEX IDX_83B2C6EC2D234F6A ON hesabdari_row
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE hesabdari_row DROP is_preview, DROP is_approved, DROP approved_by_id
SQL);
}
}

View file

@ -20,7 +20,6 @@ use Symfony\Component\Security\Http\Attribute\CurrentUser;
class ApprovalController extends AbstractController
{
// تأیید حواله انبار
#[Route('/api/approval/approve/storeroom/{ticketCode}', name: 'api_approval_approve_storeroom', methods: ['POST'])]
public function approveStoreroomTicket(
$ticketCode,
@ -30,7 +29,6 @@ class ApprovalController extends AbstractController
EntityManagerInterface $entityManager
): Response {
try {
// بررسی دسترسی کاربر
$acc = $access->hasRole('settings');
if (!$acc) {
throw $this->createAccessDeniedException();
@ -38,13 +36,11 @@ class ApprovalController extends AbstractController
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
// بررسی اینکه آیا تأیید دو مرحله‌ای فعال است
if (!$businessSettings->isRequireTwoStepApproval()) {
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
// پیدا کردن حواله انبار
$ticket = $entityManager->getRepository(\App\Entity\StoreroomTicket::class)->findOneBy([
'code' => $ticketCode,
'bid' => $business
@ -54,13 +50,11 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'حواله انبار یافت نشد']);
}
// بررسی مجوز تأیید
$canApprove = $this->canUserApproveStoreroomTicket($user, $businessSettings);
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این حواله را ندارید']);
}
// تأیید حواله
$ticket->setIsPreview(false);
$ticket->setIsApproved(true);
$ticket->setApprovedBy($user);
@ -68,7 +62,6 @@ class ApprovalController extends AbstractController
$entityManager->persist($ticket);
$entityManager->flush();
// ثبت لاگ
$logService->insert(
'تأیید حواله انبار',
"حواله انبار {$ticket->getCode()} توسط {$user->getFullName()} تأیید شد",
@ -89,7 +82,6 @@ class ApprovalController extends AbstractController
}
}
// تأیید فاکتور فروش
#[Route('/api/approval/approve/sales/{docId}', name: 'api_approval_approve_sales', methods: ['POST'])]
public function approveSalesInvoice(
$docId,
@ -99,7 +91,6 @@ class ApprovalController extends AbstractController
EntityManagerInterface $entityManager
): Response {
try {
// بررسی دسترسی کاربر
$acc = $access->hasRole('settings');
if (!$acc) {
throw $this->createAccessDeniedException();
@ -107,14 +98,12 @@ class ApprovalController extends AbstractController
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
// بررسی اینکه آیا تأیید دو مرحله‌ای فعال است
if (!$businessSettings->isRequireTwoStepApproval()) {
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
// پیدا کردن فاکتور فروش
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'code' => $docId,
'bid' => $business
]);
@ -123,13 +112,11 @@ class ApprovalController extends AbstractController
return $this->json(['success' => false, 'message' => 'فاکتور فروش یافت نشد']);
}
// بررسی مجوز تأیید
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
// تأیید فاکتور
$document->setIsPreview(false);
$document->setIsApproved(true);
$document->setApprovedBy($user);
@ -144,12 +131,22 @@ class ApprovalController extends AbstractController
$payment->setIsPreview(false);
$payment->setIsApproved(true);
$payment->setApprovedBy($user);
$entityManager->persist($payment);
}
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy([
'doc' => $document
]);
foreach ($rows as $row) {
$row->setIsPreview(false);
$row->setIsApproved(true);
$row->setApprovedBy($user);
$entityManager->persist($row);
}
$entityManager->persist($document);
$entityManager->flush();
// ثبت لاگ
$logService->insert(
'تأیید فاکتور فروش',
"فاکتور فروش {$document->getCode()} توسط {$user->getFullName()} تأیید شد",
@ -170,78 +167,8 @@ class ApprovalController extends AbstractController
}
}
// تأیید سند مالی
#[Route('/api/approval/approve/financial/{docId}', name: 'api_approval_approve_financial', methods: ['POST'])]
public function approveFinancialDocument(
$docId,
#[CurrentUser] ?User $user,
Access $access,
LogService $logService,
EntityManagerInterface $entityManager
): Response {
try {
// بررسی دسترسی کاربر
$acc = $access->hasRole('hasRole');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
// بررسی اینکه آیا تأیید دو مرحله‌ای فعال است
if (!$businessSettings->isRequireTwoStepApproval()) {
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
// پیدا کردن سند مالی
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'id' => $docId,
'bid' => $business
]);
if (!$document) {
return $this->json(['success' => false, 'message' => 'سند مالی یافت نشد']);
}
// بررسی مجوز تأیید
$canApprove = $this->canUserApproveFinancialDocument($user, $businessSettings);
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این سند را ندارید']);
}
// تأیید سند
$document->setIsPreview(false);
$document->setIsApproved(true);
$document->setApprovedBy($user);
$entityManager->persist($document);
$entityManager->flush();
// ثبت لاگ
$logService->insert(
'تأیید سند مالی',
"سند مالی {$document->getCode()} توسط {$user->getFullName()} تأیید شد",
$user,
$business
);
return $this->json([
'success' => true,
'message' => 'سند مالی با موفقیت تأیید شد'
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'message' => 'خطا در تأیید سند مالی: ' . $e->getMessage()
], 500);
}
}
#[Route('/api/approval/reject/{docId}', name: 'api_approval_reject', methods: ['POST'])]
public function rejectDocument(
$docId,
#[Route('/api/approval/approve/group/sales', name: 'api_approval_approve_group_sales', methods: ['POST'])]
public function approveSalesInvoiceGroup(
Request $request,
#[CurrentUser] ?User $user,
Access $access,
@ -249,61 +176,172 @@ class ApprovalController extends AbstractController
EntityManagerInterface $entityManager
): Response {
try {
// بررسی دسترسی کاربر
$acc = $access->hasRole('owner');
$acc = $access->hasRole('settings');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
// بررسی اینکه آیا تأیید دو مرحله‌ای فعال است
if (!$businessSettings->isRequireTwoStepApproval()) {
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
// پیدا کردن سند
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'id' => $docId,
'bid' => $business
]);
if (!$document) {
return $this->json(['success' => false, 'message' => 'سند یافت نشد']);
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتورها را ندارید']);
}
// دریافت دلیل رد
$data = json_decode($request->getContent(), true);
$rejectionReason = $data['reason'] ?? 'دلیل مشخص نشده';
$docIds = $data['docIds'] ?? [];
// رد سند
$document->setIsPreview(false);
$document->setIsApproved(false);
$document->setApprovedBy(null);
foreach ($docIds as $docId) {
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'id' => $docId,
'bid' => $business
]);
// ردیف‌ها نیازی به تنظیم جداگانه ندارند - از سند پیروی می‌کنند
if (!$document) {
return $this->json(['success' => false, 'message' => 'فاکتور فروش یافت نشد']);
}
$entityManager->persist($document);
$entityManager->flush();
if ($document->isApproved()) {
return $this->json(['success' => false, 'message' => 'فاکتور فروش تایید شده است']);
}
$document->setIsPreview(false);
$document->setIsApproved(true);
$document->setApprovedBy($user);
$payments = [];
foreach ($document->getRelatedDocs() as $relatedDoc) {
if ($relatedDoc->getType() === 'sell_receive') {
$payments[] = $relatedDoc;
}
}
foreach ($payments as $payment) {
$payment->setIsPreview(false);
$payment->setIsApproved(true);
$payment->setApprovedBy($user);
$entityManager->persist($payment);
}
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy([
'doc' => $document
]);
foreach ($rows as $row) {
$row->setIsPreview(false);
$row->setIsApproved(true);
$row->setApprovedBy($user);
$entityManager->persist($row);
}
$entityManager->persist($document);
$entityManager->flush();
}
// ثبت لاگ
$logService->insert(
'رد سند',
"سند {$document->getCode()} توسط {$user->getFullName()} رد شد. دلیل: {$rejectionReason}",
'تأیید فاکتورهای فروش',
"فاکتورهای فروش {$docIds} توسط {$user->getFullName()} تأیید شدند",
$user,
$business
);
return $this->json([
'success' => true,
'message' => 'سند با موفقیت رد شد'
'message' => 'فاکتورهای فروش با موفقیت تأیید شدند'
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'message' => 'خطا در رد سند: ' . $e->getMessage()
'message' => 'خطا در تأیید فاکتورهای فروش: ' . $e->getMessage()
], 500);
}
}
#[Route('/api/approval/unapprove/sales/{docId}', name: 'api_approval_unapprove_sales', methods: ['POST'])]
public function unapproveSalesInvoice(
$docId,
#[CurrentUser] ?User $user,
Access $access,
LogService $logService,
EntityManagerInterface $entityManager
): Response {
try {
$acc = $access->hasRole('settings');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
if (!$businessSettings->isRequireTwoStepApproval()) {
return $this->json(['success' => false, 'message' => 'تأیید دو مرحله‌ای فعال نیست']);
}
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'code' => $docId,
'bid' => $business
]);
if (!$document) {
return $this->json(['success' => false, 'message' => 'فاکتور فروش یافت نشد']);
}
$canApprove = $this->canUserApproveSalesInvoice($user, $businessSettings);
if (!$canApprove) {
return $this->json(['success' => false, 'message' => 'شما مجوز تأیید این فاکتور را ندارید']);
}
$document->setIsPreview(true);
$document->setIsApproved(false);
$document->setApprovedBy(null);
$payments = [];
foreach ($document->getRelatedDocs() as $relatedDoc) {
if ($relatedDoc->getType() === 'sell_receive') {
$payments[] = $relatedDoc;
}
}
foreach ($payments as $payment) {
$payment->setIsPreview(true);
$payment->setIsApproved(false);
$payment->setApprovedBy(null);
$entityManager->persist($payment);
}
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy([
'doc' => $document
]);
foreach ($rows as $row) {
$row->setIsPreview(true);
$row->setIsApproved(false);
$row->setApprovedBy(null);
$entityManager->persist($row);
}
$entityManager->persist($document);
$entityManager->flush();
$logService->insert(
'لغو تأیید فاکتور فروش',
"فاکتور فروش {$document->getCode()} توسط {$user->getFullName()} لغو تأیید شد",
$user,
$business
);
return $this->json([
'success' => true,
'message' => 'فاکتور فروش با موفقیت لغو تأیید شد'
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'message' => 'خطا در لغو تأیید فاکتور فروش: ' . $e->getMessage()
], 500);
}
}
@ -323,9 +361,8 @@ class ApprovalController extends AbstractController
$business = $acc['bid'];
$businessSettings = $entityManager->getRepository(Business::class)->find($business->getId());
// پیدا کردن سند
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
$document = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'id' => $docId,
'bid' => $business
]);
@ -335,7 +372,7 @@ class ApprovalController extends AbstractController
}
$canApprove = $this->canUserApproveDocument($user, $businessSettings, $document);
return $this->json([
'canApprove' => $canApprove,
'documentStatus' => [
@ -353,19 +390,14 @@ class ApprovalController extends AbstractController
}
}
/**
* بررسی اینکه آیا کاربر می‌تواند سند را تأیید کند
*/
private function canUserApproveDocument(User $user, Business $business, HesabdariDoc $document): bool
{
// مدیر کسب و کار همیشه می‌تواند تأیید کند
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
// بررسی تأییدکنندگان اختصاصی بر اساس نوع سند
$documentType = $this->getDocumentType($document);
switch ($documentType) {
case 'invoice':
return $business->getInvoiceApprover() === $user->getEmail();
@ -378,61 +410,49 @@ class ApprovalController extends AbstractController
}
}
/**
* تشخیص نوع سند
*/
private function getDocumentType(HesabdariDoc $document): string
{
$type = $document->getType();
if (strpos($type, 'sell') !== false || strpos($type, 'invoice') !== false) {
return 'invoice';
}
if (strpos($type, 'warehouse') !== false || strpos($type, 'storeroom') !== false) {
return 'warehouse';
}
if (strpos($type, 'payment') !== false || strpos($type, 'receipt') !== false || strpos($type, 'hesabdari') !== false) {
return 'financial';
}
return 'unknown';
}
// بررسی مجوز تأیید حواله انبار
private function canUserApproveStoreroomTicket(User $user, Business $business): bool
{
// مدیر کسب و کار همیشه می‌تواند تأیید کند
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
// کاربر تأییدکننده انبار
return $business->getWarehouseApprover() === $user->getEmail();
}
// بررسی مجوز تأیید فاکتور فروش
private function canUserApproveSalesInvoice(User $user, Business $business): bool
{
// مدیر کسب و کار همیشه می‌تواند تأیید کند
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
// کاربر تأییدکننده فاکتور فروش
return $business->getInvoiceApprover() === $user->getEmail();
}
// بررسی مجوز تأیید سند مالی
private function canUserApproveFinancialDocument(User $user, Business $business): bool
{
// مدیر کسب و کار همیشه می‌تواند تأیید کند
if ($user->getEmail() === $business->getOwner()->getEmail()) {
return true;
}
// کاربر تأییدکننده اسناد مالی
return $business->getFinancialApprover() === $user->getEmail();
}
}

View file

@ -163,238 +163,235 @@ class SellController extends AbstractController
return $this->json($result);
}
#[Route('/api/sell/mod', name: 'app_sell_mod')]
public function app_sell_mod(
AccountingPermissionService $accountingPermissionService,
PluginService $pluginService,
SMS $SMS,
Provider $provider,
Extractor $extractor,
Request $request,
Access $access,
Log $log,
EntityManagerInterface $entityManager,
registryMGR $registryMGR
): JsonResponse {
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
// #[Route('/api/sell/mod', name: 'app_sell_mod')]
// public function app_sell_mod(
// AccountingPermissionService $accountingPermissionService,
// PluginService $pluginService,
// SMS $SMS,
// Provider $provider,
// Extractor $extractor,
// Request $request,
// Access $access,
// Log $log,
// EntityManagerInterface $entityManager,
// registryMGR $registryMGR
// ): JsonResponse {
// $params = [];
// if ($content = $request->getContent()) {
// $params = json_decode($content, true);
// }
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
// $acc = $access->hasRole('sell');
// if (!$acc)
// throw $this->createAccessDeniedException();
$pkgcntr = $accountingPermissionService->canRegisterAccountingDoc($acc['bid']);
if ($pkgcntr['code'] == 4) {
return $this->json([
'result' => 4,
'message' => $pkgcntr['message']
]);
}
if (!array_key_exists('update', $params)) {
return $this->json($extractor->paramsNotSend());
}
if ($params['update'] != '') {
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'year' => $acc['year'],
'code' => $params['update'],
'money' => $acc['money']
]);
if (!$doc)
return $this->json($extractor->notFound());
// $pkgcntr = $accountingPermissionService->canRegisterAccountingDoc($acc['bid']);
// if ($pkgcntr['code'] == 4) {
// return $this->json([
// 'result' => 4,
// 'message' => $pkgcntr['message']
// ]);
// }
// if (!array_key_exists('update', $params)) {
// return $this->json($extractor->paramsNotSend());
// }
// if ($params['update'] != '') {
// $doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
// 'bid' => $acc['bid'],
// 'year' => $acc['year'],
// 'code' => $params['update'],
// 'money' => $acc['money']
// ]);
// if (!$doc)
// return $this->json($extractor->notFound());
// حذف سطرهای قبلی
$rows = $doc->getHesabdariRows();
foreach ($rows as $row)
$entityManager->remove($row);
// // حذف سطرهای قبلی
// $rows = $doc->getHesabdariRows();
// foreach ($rows as $row)
// $entityManager->remove($row);
// حذف سندهای پرداخت قبلی
$relatedDocs = $doc->getRelatedDocs();
foreach ($relatedDocs as $relatedDoc) {
if ($relatedDoc->getType() === 'sell_receive') {
$relatedRows = $relatedDoc->getHesabdariRows();
foreach ($relatedRows as $row) {
$entityManager->remove($row);
}
$entityManager->remove($relatedDoc);
}
}
$entityManager->flush();
} else {
$doc = new HesabdariDoc();
$doc->setBid($acc['bid']);
$doc->setYear($acc['year']);
$doc->setDateSubmit(time());
$doc->setType('sell');
$doc->setSubmitter($this->getUser());
$doc->setMoney($acc['money']);
$doc->setCode($provider->getAccountingCode($acc['bid'], 'accounting'));
// // حذف سندهای پرداخت قبلی
// $relatedDocs = $doc->getRelatedDocs();
// foreach ($relatedDocs as $relatedDoc) {
// if ($relatedDoc->getType() === 'sell_receive') {
// $relatedRows = $relatedDoc->getHesabdariRows();
// foreach ($relatedRows as $row) {
// $entityManager->remove($row);
// }
// $entityManager->remove($relatedDoc);
// }
// }
// $entityManager->flush();
// } else {
// $doc = new HesabdariDoc();
// $doc->setBid($acc['bid']);
// $doc->setYear($acc['year']);
// $doc->setDateSubmit(time());
// $doc->setType('sell');
// $doc->setSubmitter($this->getUser());
// $doc->setMoney($acc['money']);
// $doc->setCode($provider->getAccountingCode($acc['bid'], 'accounting'));
// Set approval fields based on business settings
}
if ($params['transferCost'] != 0) {
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('حمل و نقل کالا');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs($params['transferCost']);
$hesabdariRow->setBd(0);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
'code' => '61'
]);
$hesabdariRow->setRef($ref);
$entityManager->persist($hesabdariRow);
}
if ($params['discountAll'] != 0) {
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('تخفیف فاکتور');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs(0);
$hesabdariRow->setBd($params['discountAll']);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
'code' => '104'
]);
$hesabdariRow->setRef($ref);
$entityManager->persist($hesabdariRow);
// // Set approval fields based on business settings
// }
// if ($params['transferCost'] != 0) {
// $hesabdariRow = new HesabdariRow();
// $hesabdariRow->setDes('حمل و نقل کالا');
// $hesabdariRow->setBid($acc['bid']);
// $hesabdariRow->setYear($acc['year']);
// $hesabdariRow->setDoc($doc);
// $hesabdariRow->setBs($params['transferCost']);
// $hesabdariRow->setBd(0);
// $ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
// 'code' => '61'
// ]);
// $hesabdariRow->setRef($ref);
// $entityManager->persist($hesabdariRow);
// }
// if ($params['discountAll'] != 0) {
// $hesabdariRow = new HesabdariRow();
// $hesabdariRow->setDes('تخفیف فاکتور');
// $hesabdariRow->setBid($acc['bid']);
// $hesabdariRow->setYear($acc['year']);
// $hesabdariRow->setDoc($doc);
// $hesabdariRow->setBs(0);
// $hesabdariRow->setBd($params['discountAll']);
// $ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
// 'code' => '104'
// ]);
// $hesabdariRow->setRef($ref);
// $entityManager->persist($hesabdariRow);
// ذخیره نوع تخفیف و درصد آن
$doc->setDiscountType($params['discountType'] ?? 'fixed');
if (isset($params['discountPercent'])) {
$doc->setDiscountPercent((float) $params['discountPercent']);
}
}
$doc->setDes($params['des']);
$doc->setDate($params['date']);
$sumTax = 0;
$sumTotal = 0;
foreach ($params['rows'] as $row) {
$sumTax += $row['tax'];
$sumTotal += $row['sumWithoutTax'];
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes($row['des']);
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs($row['sumWithoutTax'] + $row['tax']);
$hesabdariRow->setBd(0);
$hesabdariRow->setDiscount($row['discount']);
$hesabdariRow->setTax($row['tax']);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
'code' => '53'
]);
$hesabdariRow->setRef($ref);
$row['count'] = str_replace(',', '', $row['count']);
$commodity = $entityManager->getRepository(Commodity::class)->findOneBy([
'id' => $row['commodity']['id'],
'bid' => $acc['bid']
]);
if (!$commodity)
return $this->json($extractor->paramsNotSend());
$hesabdariRow->setCommodity($commodity);
$hesabdariRow->setCommdityCount($row['count']);
$entityManager->persist($hesabdariRow);
// // ذخیره نوع تخفیف و درصد آن
// $doc->setDiscountType($params['discountType'] ?? 'fixed');
// if (isset($params['discountPercent'])) {
// $doc->setDiscountPercent((float) $params['discountPercent']);
// }
// }
// $doc->setDes($params['des']);
// $doc->setDate($params['date']);
// $sumTax = 0;
// $sumTotal = 0;
// foreach ($params['rows'] as $row) {
// $sumTax += $row['tax'];
// $sumTotal += $row['sumWithoutTax'];
// $hesabdariRow = new HesabdariRow();
// $hesabdariRow->setDes($row['des']);
// $hesabdariRow->setBid($acc['bid']);
// $hesabdariRow->setYear($acc['year']);
// $hesabdariRow->setDoc($doc);
// $hesabdariRow->setBs($row['sumWithoutTax'] + $row['tax']);
// $hesabdariRow->setBd(0);
// $hesabdariRow->setDiscount($row['discount']);
// $hesabdariRow->setTax($row['tax']);
// $ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
// 'code' => '53'
// ]);
// $hesabdariRow->setRef($ref);
// $row['count'] = str_replace(',', '', $row['count']);
// $commodity = $entityManager->getRepository(Commodity::class)->findOneBy([
// 'id' => $row['commodity']['id'],
// 'bid' => $acc['bid']
// ]);
// if (!$commodity)
// return $this->json($extractor->paramsNotSend());
// $hesabdariRow->setCommodity($commodity);
// $hesabdariRow->setCommdityCount($row['count']);
// $entityManager->persist($hesabdariRow);
if ($acc['bid']->isCommodityUpdateSellPriceAuto() == true && $commodity->getPriceSell() != $row['price']) {
$commodity->setPriceSell($row['price']);
$entityManager->persist($commodity);
}
}
$doc->setAmount($sumTax + $sumTotal - $params['discountAll'] + $params['transferCost']);
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('فاکتور فروش');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs(0);
$hesabdariRow->setBd($sumTax + $sumTotal + $params['transferCost'] - $params['discountAll']);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
'code' => '3'
]);
$hesabdariRow->setRef($ref);
$person = $entityManager->getRepository(Person::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $params['person']['code']
]);
if (!$person)
return $this->json($extractor->paramsNotSend());
$hesabdariRow->setPerson($person);
$entityManager->persist($hesabdariRow);
// if ($acc['bid']->isCommodityUpdateSellPriceAuto() == true && $commodity->getPriceSell() != $row['price']) {
// $commodity->setPriceSell($row['price']);
// $entityManager->persist($commodity);
// }
// }
// $doc->setAmount($sumTax + $sumTotal - $params['discountAll'] + $params['transferCost']);
// $hesabdariRow = new HesabdariRow();
// $hesabdariRow->setDes('فاکتور فروش');
// $hesabdariRow->setBid($acc['bid']);
// $hesabdariRow->setYear($acc['year']);
// $hesabdariRow->setDoc($doc);
// $hesabdariRow->setBs(0);
// $hesabdariRow->setBd($sumTax + $sumTotal + $params['transferCost'] - $params['discountAll']);
// $ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
// 'code' => '3'
// ]);
// $hesabdariRow->setRef($ref);
// $person = $entityManager->getRepository(Person::class)->findOneBy([
// 'bid' => $acc['bid'],
// 'code' => $params['person']['code']
// ]);
// if (!$person)
// return $this->json($extractor->paramsNotSend());
// $hesabdariRow->setPerson($person);
// $entityManager->persist($hesabdariRow);
// Two-step approval: اگر کسب‌وکار تأیید دو مرحله‌ای را الزامی کرده باشد
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
if ($businessRequire) {
$doc->setIsPreview(true);
$doc->setIsApproved(false);
$doc->setApprovedBy(null);
} else {
$doc->setIsPreview(false);
$doc->setIsApproved(true);
$doc->setApprovedBy($this->getUser());
}
$entityManager->persist($doc);
$entityManager->flush();
if (!$doc->getShortlink()) {
$doc->setShortlink($provider->RandomString(8));
}
// if ($TwoStepApproval) {
// $doc->setIsPreview(true);
// $doc->setIsApproved(false);
// $doc->setApprovedBy(null);
// } else {
// $doc->setIsPreview(false);
// $doc->setIsApproved(true);
// $doc->setApprovedBy($this->getUser());
// }
// $entityManager->persist($doc);
// $entityManager->flush();
// if (!$doc->getShortlink()) {
// $doc->setShortlink($provider->RandomString(8));
// }
if (array_key_exists('pair_docs', $params)) {
foreach ($params['pair_docs'] as $pairCode) {
$pair = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $pairCode,
]);
if ($pair) {
$pair->addRelatedDoc($doc);
}
}
}
$entityManager->persist($doc);
$entityManager->flush();
// if (array_key_exists('pair_docs', $params)) {
// foreach ($params['pair_docs'] as $pairCode) {
// $pair = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
// 'bid' => $acc['bid'],
// 'code' => $pairCode,
// ]);
// if ($pair) {
// $pair->addRelatedDoc($doc);
// }
// }
// }
// $entityManager->persist($doc);
// $entityManager->flush();
$log->insert(
'حسابداری',
'سند حسابداری شماره ' . $doc->getCode() . ' ثبت / ویرایش شد.',
$this->getUser(),
$request->headers->get('activeBid'),
$doc
);
if (array_key_exists('sms', $params)) {
if ($params['sms'] == true) {
if ($pluginService->isActive('accpro', $acc['bid']) && $person->getMobile() != '' && $acc['bid']->getTel()) {
return $this->json([
'result' =>
$SMS->sendByBalance(
[$person->getnikename(), 'sell/' . $acc['bid']->getId() . '/' . $doc->getShortlink(), $acc['bid']->getName(), $acc['bid']->getTel()],
$registryMGR->get('sms', 'plugAccproSharefaktor'),
$person->getMobile(),
$acc['bid'],
$this->getUser(),
3
)
]);
} else {
return $this->json([
'result' =>
$SMS->sendByBalance(
[$acc['bid']->getName(), 'sell/' . $acc['bid']->getId() . '/' . $doc->getShortlink()],
$registryMGR->get('sms', 'sharefaktor'),
$person->getMobile(),
$acc['bid'],
$this->getUser(),
3
)
]);
}
}
}
return $this->json($extractor->operationSuccess());
}
// $log->insert(
// 'حسابداری',
// 'سند حسابداری شماره ' . $doc->getCode() . ' ثبت / ویرایش شد.',
// $this->getUser(),
// $request->headers->get('activeBid'),
// $doc
// );
// if (array_key_exists('sms', $params)) {
// if ($params['sms'] == true) {
// if ($pluginService->isActive('accpro', $acc['bid']) && $person->getMobile() != '' && $acc['bid']->getTel()) {
// return $this->json([
// 'result' =>
// $SMS->sendByBalance(
// [$person->getnikename(), 'sell/' . $acc['bid']->getId() . '/' . $doc->getShortlink(), $acc['bid']->getName(), $acc['bid']->getTel()],
// $registryMGR->get('sms', 'plugAccproSharefaktor'),
// $person->getMobile(),
// $acc['bid'],
// $this->getUser(),
// 3
// )
// ]);
// } else {
// return $this->json([
// 'result' =>
// $SMS->sendByBalance(
// [$acc['bid']->getName(), 'sell/' . $acc['bid']->getId() . '/' . $doc->getShortlink()],
// $registryMGR->get('sms', 'sharefaktor'),
// $person->getMobile(),
// $acc['bid'],
// $this->getUser(),
// 3
// )
// ]);
// }
// }
// }
// return $this->json($extractor->operationSuccess());
// }
#[Route('/api/sell/label/change', name: 'app_sell_label_change')]
public function app_sell_label_change(Request $request, Access $access, Extractor $extractor, Log $log, EntityManagerInterface $entityManager): JsonResponse
@ -1058,6 +1055,9 @@ class SellController extends AbstractController
'message' => $pkgcntr['message']
]);
}
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$TwoStepApproval = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
try {
// بررسی وجود فاکتور برای ویرایش
@ -1100,6 +1100,15 @@ class SellController extends AbstractController
$doc->setSubmitter($this->getUser());
$doc->setMoney($acc['money']);
$doc->setCode($provider->getAccountingCode($acc['bid'], 'accounting'));
if ($TwoStepApproval) {
$doc->setIsPreview(true);
$doc->setIsApproved(false);
$doc->setApprovedBy(null);
} else {
$doc->setIsPreview(false);
$doc->setIsApproved(true);
$doc->setApprovedBy($this->getUser());
}
}
// تنظیم اطلاعات اصلی فاکتور
@ -1128,6 +1137,15 @@ class SellController extends AbstractController
$hesabdariRow->setBd(0);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '61']);
$hesabdariRow->setRef($ref);
if ($TwoStepApproval) {
$hesabdariRow->setIsPreview(true);
$hesabdariRow->setIsApproved(false);
$hesabdariRow->setApprovedBy(null);
} else {
$hesabdariRow->setIsPreview(false);
$hesabdariRow->setIsApproved(true);
$hesabdariRow->setApprovedBy($this->getUser());
}
$entityManager->persist($hesabdariRow);
}
@ -1157,6 +1175,15 @@ class SellController extends AbstractController
$hesabdariRow->setBd($totalDiscount);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '104']);
$hesabdariRow->setRef($ref);
if ($TwoStepApproval) {
$hesabdariRow->setIsPreview(true);
$hesabdariRow->setIsApproved(false);
$hesabdariRow->setApprovedBy(null);
} else {
$hesabdariRow->setIsPreview(false);
$hesabdariRow->setIsApproved(true);
$hesabdariRow->setApprovedBy($this->getUser());
}
$entityManager->persist($hesabdariRow);
}
@ -1181,6 +1208,16 @@ class SellController extends AbstractController
$hesabdariRow->setDiscountType($item['showPercentDiscount'] ? 'percent' : 'fixed');
$hesabdariRow->setDiscountPercent($item['discountPercent'] ?? 0);
if ($TwoStepApproval) {
$hesabdariRow->setIsPreview(true);
$hesabdariRow->setIsApproved(false);
$hesabdariRow->setApprovedBy(null);
} else {
$hesabdariRow->setIsPreview(false);
$hesabdariRow->setIsApproved(true);
$hesabdariRow->setApprovedBy($this->getUser());
}
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '53']);
$hesabdariRow->setRef($ref);
@ -1214,6 +1251,15 @@ class SellController extends AbstractController
$taxRow->setBd(0);
$taxRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '33']);
$taxRow->setRef($taxRef);
if ($TwoStepApproval) {
$taxRow->setIsPreview(true);
$taxRow->setIsApproved(false);
$taxRow->setApprovedBy(null);
} else {
$taxRow->setIsPreview(false);
$taxRow->setIsApproved(true);
$taxRow->setApprovedBy($this->getUser());
}
$entityManager->persist($taxRow);
}
@ -1231,6 +1277,16 @@ class SellController extends AbstractController
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
$hesabdariRow->setRef($ref);
if ($TwoStepApproval) {
$hesabdariRow->setIsPreview(true);
$hesabdariRow->setIsApproved(false);
$hesabdariRow->setApprovedBy(null);
} else {
$hesabdariRow->setIsPreview(false);
$hesabdariRow->setIsApproved(true);
$hesabdariRow->setApprovedBy($this->getUser());
}
if (!isset($params['customer']) || $params['customer'] == '') {
$person = $entityManager->getRepository(Person::class)->findOneBy([
'bid' => $acc['bid'],
@ -1256,19 +1312,6 @@ class SellController extends AbstractController
$hesabdariRow->setPerson($person);
$entityManager->persist($hesabdariRow);
// Two-step approval: اگر کسب‌وکار تأیید دو مرحله‌ای را الزامی کرده باشد
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
if ($businessRequire) {
$doc->setIsPreview(true);
$doc->setIsApproved(false);
$doc->setApprovedBy(null);
} else {
$doc->setIsPreview(false);
$doc->setIsApproved(true);
$doc->setApprovedBy($this->getUser());
}
// ذخیره فاکتور
$entityManager->persist($doc);
$entityManager->flush();
@ -1296,15 +1339,14 @@ class SellController extends AbstractController
$paymentDoc->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
$paymentDoc->setAmount($payment['amount']);
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$businessRequire = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
if ($businessRequire) {
if ($TwoStepApproval) {
$paymentDoc->setIsPreview(true);
$paymentDoc->setIsApproved(false);
$paymentDoc->setApprovedBy(null);
} else {
$paymentDoc->setIsPreview(false);
$paymentDoc->setIsApproved(true);
$paymentDoc->setApprovedBy($this->getUser());
}
// ایجاد ارتباط با فاکتور اصلی
@ -1323,6 +1365,15 @@ class SellController extends AbstractController
$bankRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '5']);
$bankRow->setRef($bankRef);
$bankRow->setBank($entityManager->getRepository(BankAccount::class)->find($payment['bank']));
if ($TwoStepApproval) {
$bankRow->setIsPreview(true);
$bankRow->setIsApproved(false);
$bankRow->setApprovedBy(null);
} else {
$bankRow->setIsPreview(false);
$bankRow->setIsApproved(true);
$bankRow->setApprovedBy($this->getUser());
}
$entityManager->persist($bankRow);
} elseif ($payment['type'] === 'cashdesk') {
// دریافت از طریق صندوق
@ -1336,6 +1387,15 @@ class SellController extends AbstractController
$cashdeskRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '121']);
$cashdeskRow->setRef($cashdeskRef);
$cashdeskRow->setCashdesk($entityManager->getRepository(Cashdesk::class)->find($payment['cashdesk']));
if ($TwoStepApproval) {
$cashdeskRow->setIsPreview(true);
$cashdeskRow->setIsApproved(false);
$cashdeskRow->setApprovedBy(null);
} else {
$cashdeskRow->setIsPreview(false);
$cashdeskRow->setIsApproved(true);
$cashdeskRow->setApprovedBy($this->getUser());
}
$entityManager->persist($cashdeskRow);
} elseif ($payment['type'] === 'salary') {
// دریافت از طریق تنخواه گردان
@ -1349,6 +1409,15 @@ class SellController extends AbstractController
$salaryRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '122']);
$salaryRow->setRef($salaryRef);
$salaryRow->setSalary($entityManager->getRepository(Salary::class)->find($payment['salary']));
if ($TwoStepApproval) {
$salaryRow->setIsPreview(true);
$salaryRow->setIsApproved(false);
$salaryRow->setApprovedBy(null);
} else {
$salaryRow->setIsPreview(false);
$salaryRow->setIsApproved(true);
$salaryRow->setApprovedBy($this->getUser());
}
$entityManager->persist($salaryRow);
}
@ -1363,6 +1432,15 @@ class SellController extends AbstractController
$receiveRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
$receiveRow->setRef($receiveRef);
$receiveRow->setPerson($person);
if ($TwoStepApproval) {
$receiveRow->setIsPreview(true);
$receiveRow->setIsApproved(false);
$receiveRow->setApprovedBy(null);
} else {
$receiveRow->setIsPreview(false);
$receiveRow->setIsApproved(true);
$receiveRow->setApprovedBy($this->getUser());
}
$entityManager->persist($receiveRow);
$entityManager->persist($paymentDoc);

View file

@ -369,5 +369,47 @@ class HesabdariRow
return $this;
}
// Approval fields
#[ORM\Column(nullable: true)]
private ?bool $isPreview = null;
#[ORM\Column(nullable: true)]
private ?bool $isApproved = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: true)]
private ?User $approvedBy = null;
public function isPreview(): ?bool
{
return $this->isPreview;
}
public function setIsPreview(?bool $isPreview): static
{
$this->isPreview = $isPreview;
return $this;
}
public function isApproved(): ?bool
{
return $this->isApproved;
}
public function setIsApproved(?bool $isApproved): static
{
$this->isApproved = $isApproved;
return $this;
}
public function getApprovedBy(): ?User
{
return $this->approvedBy;
}
public function setApprovedBy(?User $approvedBy): static
{
$this->approvedBy = $approvedBy;
return $this;
}
}

View file

@ -1,43 +1,43 @@
<?php
namespace App\Repository;
// namespace App\Repository;
use App\Entity\HesabdariDoc;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
// use App\Entity\HesabdariDoc;
// use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
// use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<HesabdariDoc>
*
* @method HesabdariDoc|null find($id, $lockMode = null, $lockVersion = null)
* @method HesabdariDoc|null findOneBy(array $criteria, array $orderBy = null)
* @method HesabdariDoc[] findAll()
* @method HesabdariDoc[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class HesabdariDocRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, HesabdariDoc::class);
}
// /**
// * @extends ServiceEntityRepository<HesabdariDoc>
// *
// * @method HesabdariDoc|null find($id, $lockMode = null, $lockVersion = null)
// * @method HesabdariDoc|null findOneBy(array $criteria, array $orderBy = null)
// * @method HesabdariDoc[] findAll()
// * @method HesabdariDoc[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
// */
// class HesabdariDocRepository extends ServiceEntityRepository
// {
// public function __construct(ManagerRegistry $registry)
// {
// parent::__construct($registry, HesabdariDoc::class);
// }
public function save(HesabdariDoc $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
// public function save(HesabdariDoc $entity, bool $flush = false): void
// {
// $this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
// if ($flush) {
// $this->getEntityManager()->flush();
// }
// }
public function remove(HesabdariDoc $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
// public function remove(HesabdariDoc $entity, bool $flush = false): void
// {
// $this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
// if ($flush) {
// $this->getEntityManager()->flush();
// }
// }
// /**
// * @return HesabdariDoc[] Returns an array of HesabdariDoc objects
@ -63,4 +63,116 @@ class HesabdariDocRepository extends ServiceEntityRepository
// ->getOneOrNullResult()
// ;
// }
}
// }
namespace App\Repository;
use App\Entity\HesabdariDoc;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class HesabdariDocRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, HesabdariDoc::class);
}
public function save(HesabdariDoc $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(HesabdariDoc $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function find(mixed $id, \Doctrine\DBAL\LockMode|int|null $lockMode = null, ?int $lockVersion = null): ?object
{
return $this->createQueryBuilder('h')
->andWhere('h.id = :id')
->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
public function findOneBy(array $criteria, array $orderBy = null): ?object
{
$qb = $this->createQueryBuilder('h');
foreach ($criteria as $field => $value) {
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
}
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
if ($orderBy) {
foreach ($orderBy as $field => $direction) {
$qb->addOrderBy("h.$field", $direction);
}
}
return $qb->getQuery()->getOneOrNullResult();
}
//include preview
public function findOneByIncludePreview(array $criteria, array $orderBy = null): ?object
{
$qb = $this->createQueryBuilder('h');
foreach ($criteria as $field => $value) {
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
}
if ($orderBy) {
foreach ($orderBy as $field => $direction) {
$qb->addOrderBy("h.$field", $direction);
}
}
return $qb->getQuery()->getOneOrNullResult();
}
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array
{
$qb = $this->createQueryBuilder('h');
foreach ($criteria as $field => $value) {
$qb->andWhere("h.$field = :$field")->setParameter($field, $value);
}
$qb->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))');
if ($orderBy) {
foreach ($orderBy as $field => $direction) {
$qb->addOrderBy("h.$field", $direction);
}
}
if ($limit) {
$qb->setMaxResults($limit);
}
if ($offset) {
$qb->setFirstResult($offset);
}
return $qb->getQuery()->getResult();
}
public function findAll(): array
{
return $this->createQueryBuilder('h')
->andWhere('(h.isApproved = 1 OR (h.isApproved = 0 AND h.isPreview = 0))')
->getQuery()
->getResult();
}
}

View file

@ -157,6 +157,12 @@
<v-icon color="success">mdi-check-decagram</v-icon>
</template>
</v-list-item>
<v-list-item v-if="canShowUnapproveButton(item)" class="text-dark" title="لغو تایید فاکتور"
@click="unapproveInvoice(item.code)">
<template v-slot:prepend>
<v-icon color="red">mdi-cancel</v-icon>
</template>
</v-list-item>
<v-list-item class="text-dark" :title="$t('dialog.edit')" @click="canEditItem(item.code)">
<template v-slot:prepend>
<v-icon icon="mdi-file-edit"></v-icon>
@ -417,7 +423,7 @@ export default defineComponent({
serverOptions: reactive({
page: 1,
rowsPerPage: 10,
sortBy: [], // برای پشتیبانی از Multi-Sort
sortBy: [],
}),
allHeaders: [
{ title: "جزئیات", value: "expand", sortable: false, visible: true, width: 80 },
@ -449,7 +455,6 @@ export default defineComponent({
computed: {
visibleHeaders() {
return this.allHeaders.filter(header => {
// اگر ستونهای تأیید هستند، باید دو مرحلهای فعال باشد
if ((header.value === 'approvalStatus' || header.value === 'approvedBy') && !this.business.requireTwoStepApproval) {
return false;
}
@ -474,14 +479,20 @@ export default defineComponent({
Swal.fire({ text: 'هیچ موردی انتخاب نشده است.', icon: 'warning', confirmButtonText: 'قبول' });
return;
}
const selectedInvoices = this.items.filter(inv => this.itemsSelected.includes(inv.code));
if (selectedInvoices.some(inv => !(!inv.isApproved && inv.isPreview))) {
Swal.fire({ text: 'برخی فاکتور های انتخابی تایید شده هستند.', icon: 'warning', confirmButtonText: 'قبول' });
return;
}
Swal.fire({ title: 'تایید فاکتورهای انتخابی', text: 'فاکتورهای انتخاب‌شده تایید خواهند شد.', icon: 'question', showCancelButton: true, confirmButtonText: 'بله', cancelButtonText: 'خیر' })
.then(async (r) => {
if (!r.isConfirmed) return;
this.loading = true;
try {
for (const code of this.itemsSelected) {
await axios.post(`/api/sell/approve/${code}`);
}
await axios.post(`/api/approval/approve/group/sales`, {
'docIds': this.itemsSelected
});
Swal.fire({ text: 'فاکتورها تایید شدند.', icon: 'success', confirmButtonText: 'قبول' });
this.itemsSelected = [];
this.loadData();
@ -504,7 +515,6 @@ export default defineComponent({
this.plugins = {};
}
},
// بارگذاری اطلاعات بیزنس
async loadBusinessInfo() {
try {
const response = await axios.get('/api/business/get/info/' + localStorage.getItem('activeBid'));
@ -514,7 +524,6 @@ export default defineComponent({
this.business = { requireTwoStepApproval: false, invoiceApprover: null };
}
},
// بارگذاری اطلاعات کاربر فعلی
async loadCurrentUser() {
try {
const response = await axios.post('/api/business/get/user/permissions');
@ -584,12 +593,12 @@ export default defineComponent({
perPage: this.serverOptions.rowsPerPage,
types: this.types.filter(t => t.checked).map(t => t.code),
dateFilter: this.dateFilter,
sortBy: this.serverOptions.sortBy, // ارسال اطلاعات مرتبسازی
sortBy: this.serverOptions.sortBy,
});
const all = (response.data.items || []).map(item => ({
...item,
receivedAmount: item.relatedDocsPays || 0, // نگاشت به receivedAmount
receivedAmount: item.relatedDocsPays || 0,
})).filter(item => item.code && typeof item.code !== 'undefined');
this.items = all;
this.total = Number(response.data.total) || 0;
@ -614,7 +623,6 @@ export default defineComponent({
this.loading = false;
}
},
// نمایش متن وضعیت تأیید
getApprovalStatusText(item) {
if (!this.business?.requireTwoStepApproval) return 'تایید دو مرحله‌ای غیرفعال';
@ -622,7 +630,6 @@ export default defineComponent({
if (item.isApproved) return 'تایید شده';
return 'تایید شده';
},
// نمایش رنگ وضعیت تأیید
getApprovalStatusColor(item) {
if (!this.business?.requireTwoStepApproval) return 'default';
@ -630,24 +637,18 @@ export default defineComponent({
if (item.isApproved) return 'success';
return 'success';
},
// بررسی اینکه آیا دکمه تأیید باید نمایش داده شود
canShowApprovalButton(item) {
if (!this.business?.requireTwoStepApproval) return false;
// اگر سند قبلاً تأیید شده، دکمه تأیید نمایش داده نشود
if (item?.isApproved) return false;
if (item?.isApproved || (!item?.isPreview && !item?.isApproved)) return false;
// مدیر کسب و کار همیشه میتواند تأیید کند
// یا کاربر تأییدکننده فاکتور فروش
return this.business?.invoiceApprover === this.currentUser?.email || this.currentUser?.owner === true;
},
// تایید فاکتور فروش
async approveInvoice(code) {
try {
this.loading = true;
await axios.post(`/api/approval/approve/sales/${code}`);
// بهروزرسانی دادهها
await this.loadData();
Swal.fire({ text: 'فاکتور تایید شد', icon: 'success', confirmButtonText: 'قبول' });
@ -657,6 +658,23 @@ export default defineComponent({
this.loading = false;
}
},
canShowUnapproveButton(item) {
return !this.canShowApprovalButton(item);
},
async unapproveInvoice(code) {
try {
this.loading = true;
await axios.post(`/api/approval/unapprove/sales/${code}`);
await this.loadData();
Swal.fire({ text: 'تایید فاکتور لغو شد', icon: 'success', confirmButtonText: 'قبول' });
} catch (error) {
Swal.fire({ text: 'خطا در لغو تایید فاکتور: ' + (error.response?.data?.message || error.message), icon: 'error', confirmButtonText: 'قبول' });
} finally {
this.loading = false;
}
},
canEditItem(code) {
this.loading = true;
axios.post('/api/sell/edit/can/' + code).then((response) => {
@ -734,14 +752,12 @@ export default defineComponent({
}).then(async (response) => {
try {
if (response.data && response.data.id) {
// دریافت فایل PDF
const pdfResponse = await axios({
method: 'get',
url: '/front/print/' + response.data.id,
responseType: 'arraybuffer'
});
// ایجاد لینک دانلود
var fileURL = window.URL.createObjectURL(new Blob([pdfResponse.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
@ -824,7 +840,7 @@ export default defineComponent({
updateServerOptions(options) {
this.serverOptions.page = options.page;
this.serverOptions.rowsPerPage = options.itemsPerPage;
this.serverOptions.sortBy = options.sortBy || []; // مدیریت Multi-Sort
this.serverOptions.sortBy = options.sortBy || [];
this.loadData();
},
debouncedLoadData: debounce(function () {