hesabixCore/hesabixCore/src/Controller/SellController.php
2025-10-01 13:40:14 +03:30

2102 lines
91 KiB
PHP

<?php
namespace App\Controller;
use App\Entity\Business;
use App\Service\AccountingPermissionService;
use App\Service\Jdate;
use App\Service\Log;
use App\Service\Access;
use App\Service\Explore;
use App\Entity\Commodity;
use App\Service\Provider;
use App\Service\Extractor;
use App\Entity\HesabdariDoc;
use App\Entity\HesabdariRow;
use App\Entity\HesabdariTable;
use App\Entity\InvoiceType;
use App\Entity\Person;
use App\Entity\PrintOptions;
use App\Entity\StoreroomTicket;
use App\Service\Printers;
use App\Service\registryMGR;
use App\Service\SMS;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\BankAccount;
use App\Entity\Cashdesk;
use App\Entity\Salary;
use App\Entity\Year;
use App\Entity\CustomInvoiceTemplate;
use App\Service\CustomInvoice\TemplateRenderer;
use App\Service\PluginService;
class SellController extends AbstractController
{
#[Route('/api/sell/edit/can/{code}', name: 'app_sell_can_edit')]
public function app_sell_can_edit(Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, string $code): JsonResponse
{
$canEdit = true;
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money']
]);
if (!$doc) {
$canEdit = false;
}
$year = $entityManager->getRepository(Year::class)->findOneBy([
'bid' => $acc['bid'],
'head' => true
]);
if ($doc->getYear()->getId() != $year->getId()) {
$canEdit = false;
}
$tickets = $entityManager->getRepository(StoreroomTicket::class)->findBy(['doc' => $doc]);
if (count($tickets) != 0)
$canEdit = false;
return $this->json([
'result' => $canEdit
]);
}
#[Route('/api/sell/approve/{code}', name: 'app_sell_approve', methods: ['POST'])]
public function approveSellDoc(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc) throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money']
]);
if (!$doc) throw $this->createNotFoundException('فاکتور یافت نشد');
$doc->setStatus('approved');
$entityManager->persist($doc);
$entityManager->flush();
return $this->json(['result' => 0]);
}
#[Route('/api/sell/payment/approve/{code}', name: 'app_sell_payment_approve', methods: ['POST'])]
public function approveSellPayment(string $code, Request $request, Access $access, EntityManagerInterface $entityManager): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc) throw $this->createAccessDeniedException();
$paymentDoc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money'],
'type' => 'sell_receive'
]);
if (!$paymentDoc) throw $this->createNotFoundException('سند دریافت یافت نشد');
$paymentDoc->setStatus('approved');
$entityManager->persist($paymentDoc);
$entityManager->flush();
return $this->json(['result' => 0]);
}
#[Route('/api/sell/get/info/{code}', name: 'app_sell_get_info')]
public function app_sell_get_info(Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, string $code): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money']
]);
if (!$doc)
throw $this->createNotFoundException();
$result = Explore::ExploreSellDoc($doc);
$profit = 0;
foreach ($doc->getHesabdariRows() as $item) {
if ($item->getCommodity() && $item->getCommdityCount()) {
if ($acc['bid']->getProfitCalctype() == 'simple') {
$profit = $profit + (($item->getCommodity()->getPriceSell() - $item->getCommodity()->getPriceBuy()) * $item->getCommdityCount());
} elseif ($acc['bid']->getProfitCalctype() == 'lis') {
$last = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
'commodity' => $item->getCommodity(),
'bs' => 0
], [
'id' => 'DESC'
]);
if ($last) {
$price = $last->getBd() / $last->getCommdityCount();
$profit = $profit + (($item->getBs() / $item->getCommdityCount() - $price) * $item->getCommdityCount());
} else {
$profit = $profit + $item->getBs();
}
} else {
$lasts = $entityManager->getRepository(HesabdariRow::class)->findBy([
'commodity' => $item->getCommodity(),
'bs' => 0
], [
'id' => 'DESC'
]);
$avg = 0;
$count = 0;
foreach ($lasts as $last) {
$avg = $avg + $last->getBd();
$count = $count + $last->getCommdityCount();
}
if ($count != 0) {
$price = $avg / $count;
$profit = $profit + (($item->getBs() / $item->getCommdityCount() - $price) * $item->getCommdityCount());
} else {
$profit = $profit + $item->getBs();
}
}
$profit = round($profit);
}
}
$result['profit'] = $profit;
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);
// }
// $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());
// // حذف سطرهای قبلی
// $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'));
// // 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);
// 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 ($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();
// $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
{
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
if ($params['label'] != 'clear') {
$label = $entityManager->getRepository(InvoiceType::class)->findOneBy([
'code' => $params['label']['code'],
'type' => 'sell'
]);
if (!$label)
return $this->json($extractor->notFound());
}
foreach ($params['items'] as $item) {
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'year' => $acc['year'],
'code' => $item['code'],
'money' => $acc['money']
]);
if (!$doc)
return $this->json($extractor->notFound());
if ($params['label'] != 'clear') {
$doc->setInvoiceLabel($label);
$entityManager->persist($doc);
$log->insert(
'حسابداری',
' تغییر برچسب فاکتور‌ شماره ' . $doc->getCode() . ' به ' . $label->getLabel(),
$this->getUser(),
$acc['bid']->getId(),
$doc
);
} else {
$doc->setInvoiceLabel(null);
$entityManager->persist($doc);
$log->insert(
'حسابداری',
' حذف برچسب فاکتور‌ شماره ' . $doc->getCode(),
$this->getUser(),
$acc['bid']->getId(),
$doc
);
}
}
$entityManager->flush();
return $this->json($extractor->operationSuccess());
}
#[Route('/api/sell/docs/search', name: 'app_sell_docs_search', methods: ['POST'])]
public function searchSellDocs(
Provider $provider,
Request $request,
Access $access,
Log $log,
EntityManagerInterface $entityManager,
Jdate $jdate
): JsonResponse {
$acc = $access->hasRole('sell');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$params = json_decode($request->getContent(), true) ?? [];
$searchTerm = $params['search'] ?? '';
$page = max(1, $params['page'] ?? 1);
$perPage = max(1, min(100, $params['perPage'] ?? 10));
$types = $params['types'] ?? [];
$dateFilter = $params['dateFilter'] ?? 'all';
$sortBy = $params['sortBy'] ?? [];
$queryBuilder = $entityManager->createQueryBuilder()
->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount')
->addSelect('d.isPreview, d.isApproved')
->addSelect('u.fullName as submitter')
->addSelect('approver.fullName as approvedByName, approver.id as approvedById, approver.email as approvedByEmail')
->addSelect('l.code as labelCode, l.label as labelLabel')
->from(HesabdariDoc::class, 'd')
->leftJoin('d.submitter', 'u')
->leftJoin('d.approvedBy', 'approver')
->leftJoin('d.InvoiceLabel', 'l')
->leftJoin('d.hesabdariRows', 'r')
->where('d.bid = :bid')
->andWhere('d.year = :year')
->andWhere('d.type = :type')
->andWhere('d.money = :money')
->setParameter('bid', $acc['bid'])
->setParameter('year', $acc['year'])
->setParameter('type', 'sell')
->setParameter('money', $acc['money']);
// اعمال فیلترهای تاریخ
$today = $jdate->jdate('Y/m/d', time());
if ($dateFilter === 'today') {
$queryBuilder->andWhere('d.date = :today')
->setParameter('today', $today);
} elseif ($dateFilter === 'week') {
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
->setParameter('weekStart', $weekStart)
->setParameter('today', $today);
} elseif ($dateFilter === 'month') {
$monthStart = $jdate->jdate('Y/m/01', time());
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
->setParameter('monthStart', $monthStart)
->setParameter('today', $today);
}
if ($searchTerm) {
$queryBuilder->leftJoin('r.person', 'p')
->andWhere(
$queryBuilder->expr()->orX(
'd.code LIKE :search',
'd.des LIKE :search',
'd.date LIKE :search',
'd.amount LIKE :search',
'p.nikename LIKE :search',
'p.mobile LIKE :search'
)
)
->setParameter('search', "%$searchTerm%");
}
if (!empty($types)) {
$queryBuilder->andWhere('l.code IN (:types)')
->setParameter('types', $types);
}
// فیلدهای معتبر برای مرتب‌سازی توی دیتابیس
$validDbFields = [
'id' => 'd.id',
'dateSubmit' => 'd.dateSubmit',
'date' => 'd.date',
'type' => 'd.type',
'code' => 'd.code',
'des' => 'd.des',
'amount' => 'd.amount',
'mdate' => 'd.mdate',
'plugin' => 'd.plugin',
'refData' => 'd.refData',
'shortlink' => 'd.shortlink',
'isPreview' => 'd.isPreview',
'isApproved' => 'd.isApproved',
'approvedBy' => 'd.approvedBy',
'submitter' => 'u.fullName',
'label' => 'l.label', // از InvoiceLabel
];
// اعمال مرتب‌سازی توی دیتابیس
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
if ($key === 'profit' || $key === 'receivedAmount') {
continue; // این‌ها توی PHP مرتب می‌شن
} elseif (isset($validDbFields[$key])) {
$queryBuilder->addOrderBy($validDbFields[$key], $direction);
}
// اگه کلید معتبر نبود، نادیده گرفته می‌شه
}
} else {
$queryBuilder->orderBy('d.id', 'DESC');
}
$totalItemsQuery = clone $queryBuilder;
$totalItems = $totalItemsQuery->select('COUNT(DISTINCT d.id)')
->getQuery()
->getSingleScalarResult();
$queryBuilder->setFirstResult(($page - 1) * $perPage)
->setMaxResults($perPage);
$docs = $queryBuilder->getQuery()->getArrayResult();
$dataTemp = [];
foreach ($docs as $doc) {
$item = [
'id' => $doc['id'],
'dateSubmit' => $doc['dateSubmit'],
'date' => $doc['date'],
'type' => $doc['type'],
'code' => $doc['code'],
'des' => $doc['des'],
'amount' => $doc['amount'],
'submitter' => $doc['submitter'],
'label' => $doc['labelCode'] ? [
'code' => $doc['labelCode'],
'label' => $doc['labelLabel']
] : null,
'isPreview' => $doc['isPreview'],
'isApproved' => $doc['isApproved'],
'approvedBy' => $doc['approvedByName'] ? [
'fullName' => $doc['approvedByName'],
'id' => $doc['approvedById'],
'email' => $doc['approvedByEmail']
] : null,
];
$mainRow = $entityManager->getRepository(HesabdariRow::class)
->createQueryBuilder('r')
->where('r.doc = :docId')
->andWhere('r.person IS NOT NULL')
->setParameter('docId', $doc['id'])
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$item['person'] = $mainRow && $mainRow->getPerson() ? [
'id' => $mainRow->getPerson()->getId(),
'nikename' => $mainRow->getPerson()->getNikename(),
'code' => $mainRow->getPerson()->getCode()
] : null;
// استفاده از SQL خام برای محاسبه پرداختی‌ها
$sql = "
SELECT SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs
FROM hesabdari_doc rd
JOIN hesabdari_doc_hesabdari_doc rel ON rel.hesabdari_doc_target = rd.id
WHERE rel.hesabdari_doc_source = :sourceDocId
AND rd.bid_id = :bidId
";
$stmt = $entityManager->getConnection()->prepare($sql);
$stmt->bindValue('sourceDocId', $doc['id']);
$stmt->bindValue('bidId', $acc['bid']->getId());
$result = $stmt->executeQuery()->fetchAssociative();
$relatedDocsPays = $result['total_pays'] ?? 0;
$relatedDocsCount = $result['count_docs'] ?? 0;
$item['relatedDocsCount'] = (int) $relatedDocsCount;
$item['relatedDocsPays'] = $relatedDocsPays;
$item['profit'] = $this->calculateProfit($doc['id'], $acc, $entityManager);
$item['discountAll'] = 0;
$item['transferCost'] = 0;
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
foreach ($rows as $row) {
if ($row->getRef()->getCode() == '104') {
$item['discountAll'] = $row->getBd();
} elseif ($row->getRef()->getCode() == '61') {
$item['transferCost'] = $row->getBs();
}
}
$dataTemp[] = $item;
}
// مرتب‌سازی توی PHP برای profit و receivedAmount
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? SORT_DESC : SORT_ASC;
if ($key === 'profit') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['profit'] - $b['profit'] : $b['profit'] - $a['profit'];
});
} elseif ($key === 'receivedAmount') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['relatedDocsPays'] - $b['relatedDocsPays'] : $b['relatedDocsPays'] - $a['relatedDocsPays'];
});
}
}
}
return $this->json([
'items' => $dataTemp,
'total' => (int) $totalItems,
'page' => $page,
'perPage' => $perPage,
]);
}
private function calculateProfit(int $docId, array $acc, EntityManagerInterface $entityManager): int
{
$profit = 0;
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $docId]);
foreach ($rows as $item) {
if ($item->getCommdityCount() && $item->getBs()) {
$commodityId = $item->getCommodity() ? $item->getCommodity()->getId() : null;
if ($acc['bid']->getProfitCalctype() === 'lis') {
if ($commodityId) {
$last = $entityManager->getRepository(HesabdariRow::class)
->findOneBy(['commodity' => $commodityId, 'bs' => 0], ['id' => 'DESC']);
if ($last && $last->getCommdityCount() > 0 && $item->getCommdityCount() > 0) {
$price = $last->getBd() / $last->getCommdityCount();
$profit += ($item->getBs() / $item->getCommdityCount() - $price) * $item->getCommdityCount();
} else {
$profit += $item->getBs();
}
} else {
$profit += $item->getBs();
}
} elseif ($acc['bid']->getProfitCalctype() === 'simple') {
if ($item->getCommodity() && $item->getCommodity()->getPriceSell() !== null && $item->getCommodity()->getPriceBuy() !== null) {
$profit += ($item->getCommodity()->getPriceSell() - $item->getCommodity()->getPriceBuy()) * $item->getCommdityCount();
} else {
$profit += $item->getBs();
}
} else {
if ($commodityId) {
$lasts = $entityManager->getRepository(HesabdariRow::class)
->findBy(['commodity' => $commodityId, 'bs' => 0], ['id' => 'DESC']);
$avg = 0;
$count = 0;
foreach ($lasts as $last) {
$avg += $last->getBd();
$count += $last->getCommdityCount();
}
if ($count != 0 && $item->getCommdityCount() > 0) {
$price = $avg / $count;
$profit += ($item->getBs() / $item->getCommdityCount() - $price) * $item->getCommdityCount();
} else {
$profit += $item->getBs();
}
} else {
$profit += $item->getBs();
}
}
}
}
return round($profit);
}
#[Route('/api/sell/rows/{code}', name: 'app_sell_rows', methods: ['GET'])]
public function getSellRows(
Request $request,
Access $access,
EntityManagerInterface $entityManager,
string $code,
Log $log // سرویس Log را اضافه کنید
): JsonResponse {
$acc = $access->hasRole('sell');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $code,
'money' => $acc['money'],
]);
if (!$doc) {
$log->insert('SellController', 'Doc not found for code: ' . $code, $this->getUser(), $acc['bid']->getId());
throw $this->createNotFoundException();
}
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
$data = array_map(function ($row) use ($log) {
try {
return [
'id' => $row->getId(),
'des' => $row->getDes(),
'bs' => $row->getBs(),
'commdityCount' => $row->getCommdityCount(),
'commodity' => $row->getCommodity() ? [
'id' => $row->getCommodity()->getId(),
'name' => $row->getCommodity()->getName(),
] : null,
];
} catch (\Exception $e) {
$log->insert('SellController', 'Error processing row: ' . $e->getMessage(), $this->getUser(), null);
return null;
}
}, $rows);
// فیلتر کردن موارد null
$data = array_filter($data);
return $this->json(['rows' => array_values($data)]);
}
#[Route('/api/sell/print/invoice', name: 'app_sell_print_invoice')]
public function app_sell_print_invoice(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, PluginService $pluginService, TemplateRenderer $renderer): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
$params = json_decode($request->getContent(), true);
$params['printers'] = $params['printers'] ?? false;
$params['pdf'] = $params['pdf'] ?? true;
$params['posPrint'] = $params['posPrint'] ?? false;
// دریافت تنظیمات پیش‌فرض از PrintOptions
$printSettings = $entityManager->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]);
// تنظیم مقادیر پیش‌فرض از تنظیمات ذخیره شده
$defaultOptions = [
'note' => $printSettings ? $printSettings->isSellNote() : true,
'bidInfo' => $printSettings ? $printSettings->isSellBidInfo() : true,
'taxInfo' => $printSettings ? $printSettings->isSellTaxInfo() : true,
'discountInfo' => $printSettings ? $printSettings->isSellDiscountInfo() : true,
'pays' => $printSettings ? $printSettings->isSellPays() : true,
'paper' => $printSettings ? $printSettings->getSellPaper() : 'A4-L',
'invoiceIndex' => $printSettings ? $printSettings->isSellInvoiceIndex() : true,
'businessStamp' => $printSettings ? $printSettings->isSellBusinessStamp() : true
];
// اولویت با پارامترهای ارسالی است
$printOptions = array_merge($defaultOptions, $params['printOptions'] ?? []);
$docRepo = $entityManager->getRepository(HesabdariDoc::class);
$doc = $docRepo->findOneBy([
'bid' => $acc['bid'],
'code' => $params['code'],
'money' => $acc['money']
]);
// Fallback در صورت ناهمخوانی activeMoney
if (!$doc) {
$doc = $docRepo->findOneBy([
'bid' => $acc['bid'],
'code' => $params['code'],
'year' => $acc['year']
]);
}
// Fallback با QueryBuilder بر اساس شناسه BID (عدم تکیه بر شیء)
if (!$doc) {
try {
$doc = $entityManager->createQueryBuilder()
->select('d')
->from(\App\Entity\HesabdariDoc::class, 'd')
->where('d.code = :code')
->andWhere('IDENTITY(d.bid) = :bidId')
->setParameter('code', (string)($params['code'] ?? ''))
->setParameter('bidId', $acc['bid']->getId())
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
} catch (\Exception $e) {
$doc = null;
}
}
// Fallback نهایی فقط بر اساس کد (در صورت وجود چند کسب‌وکار، کنترل امنیت بعدی برقرار است)
if (!$doc) {
try {
$doc = $entityManager->createQueryBuilder()
->select('d')
->from(\App\Entity\HesabdariDoc::class, 'd')
->where('d.code = :code')
->setParameter('code', (string)($params['code'] ?? ''))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
if ($doc && $doc->getBid()->getId() !== $acc['bid']->getId()) {
// سند پیدا شد ولی متعلق به کسب‌وکار دیگری است
throw $this->createAccessDeniedException();
}
} catch (\Exception $e) {
$doc = null;
}
}
if (!$doc)
throw $this->createNotFoundException();
$person = null;
$discount = 0;
$transfer = 0;
foreach ($doc->getHesabdariRows() as $item) {
if ($item->getPerson()) {
$person = $item->getPerson();
} elseif ($item->getRef()->getCode() == 104) {
$discount = $item->getBd();
} elseif ($item->getRef()->getCode() == 61) {
$transfer = $item->getBs();
}
}
$pdfPid = 0;
// فیلد جدید وضعیت حساب مشتری
$personItems = $entityManager->getRepository(HesabdariRow::class)->findBy(['bid' => $acc['bid'], 'person' => $person]);
$accountStatus = [];
$bs = 0;
$bd = 0;
foreach ($personItems as $item) {
$bs += $item->getBs();
$bd += $item->getBd();
}
if ($bs > $bd) {
$accountStatus['label'] = 'بستانکار';
$accountStatus['value'] = $bs - $bd;
} else {
$accountStatus['label'] = 'بدهکار';
$accountStatus['value'] = $bd - $bs;
}
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$twoApproval = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
if ($twoApproval && $doc->isApproved() !== true && $doc->isPreview() == true) {
return $this->json(['result' => -10, 'message' => 'فاکتور هنوز تایید نشده است'], 403);
}
if ($params['pdf'] == true || $params['printers'] == true) {
$note = '';
if ($printSettings) {
$note = $printSettings->getSellNoteString();
}
// Build safe context data for rendering
$rowsArr = array_map(function ($row) {
return [
'commodity' => $row->getCommodity() ? [
'name' => method_exists($row->getCommodity(), 'getName') ? $row->getCommodity()->getName() : null,
'code' => method_exists($row->getCommodity(), 'getCode') ? $row->getCommodity()->getCode() : null,
] : null,
'commodityCount' => $row->getCommdityCount(),
'des' => $row->getDes(),
'bs' => $row->getBs(),
'tax' => $row->getTax(),
'discount' => $row->getDiscount(),
'showPercentDiscount' => $row->getDiscountType() === 'percent',
'discountPercent' => $row->getDiscountPercent()
];
}, $doc->getHesabdariRows()->toArray());
$personArr = $person ? [
'name' => $person->getName(),
'mobile' => $person->getMobile(),
'tel' => $person->getTel(),
'address' => $person->getAddress(),
] : null;
$biz = $acc['bid'];
$businessArr = $biz ? [
'name' => method_exists($biz, 'getName') ? $biz->getName() : null,
'tel' => method_exists($biz, 'getTel') ? $biz->getTel() : null,
'mobile' => method_exists($biz, 'getMobile') ? $biz->getMobile() : null,
'address' => method_exists($biz, 'getAddress') ? $biz->getAddress() : null,
'shenasemeli' => method_exists($biz, 'getShenasemeli') ? $biz->getShenasemeli() : null,
'codeeghtesadi' => method_exists($biz, 'getCodeeghtesadi') ? $biz->getCodeeghtesadi() : null,
'id' => method_exists($biz, 'getId') ? $biz->getId() : null,
] : null;
$context = [
'accountStatus' => $accountStatus,
'business' => $businessArr,
'bid' => $businessArr,
'doc' => [
'code' => $doc->getCode(),
'date' => method_exists($doc, 'getDate') ? $doc->getDate() : null,
'taxPercent' => method_exists($doc, 'getTaxPercent') ? $doc->getTaxPercent() : null,
'discountPercent' => $doc->getDiscountPercent(),
'discountType' => $doc->getDiscountType(),
'amount' => $doc->getAmount(),
'money' => [
'shortName' => method_exists($doc, 'getMoney') && $doc->getMoney() && method_exists($doc->getMoney(), 'getShortName') ? $doc->getMoney()->getShortName() : null,
],
],
'rows' => $rowsArr,
'person' => $personArr,
'discount' => $discount,
'transfer' => $transfer,
'printOptions' => $printOptions,
'note' => $note,
];
// Decide template: custom or default
$html = null;
$isCustomInvoiceActive = $pluginService->isActive('custominvoice', $acc['bid']);
$selectedTemplate = $printSettings ? $printSettings->getSellTemplate() : null;
if ($isCustomInvoiceActive && $selectedTemplate instanceof CustomInvoiceTemplate) {
$html = $renderer->render($selectedTemplate->getCode() ?? '', $context);
}
if ($html === null) {
// fallback to default Twig template
$html = $this->renderView('pdf/printers/sell.html.twig', [
'accountStatus' => $accountStatus,
'bid' => $acc['bid'],
'doc' => $doc,
'rows' => array_map(function ($row) {
return [
'commodity' => $row->getCommodity(),
'commodityCount' => $row->getCommdityCount(),
'des' => $row->getDes(),
'bs' => $row->getBs(),
'tax' => $row->getTax(),
'discount' => $row->getDiscount(),
'showPercentDiscount' => $row->getDiscountType() === 'percent',
'discountPercent' => $row->getDiscountPercent()
];
}, $doc->getHesabdariRows()->toArray()),
'person' => $person,
'printInvoice' => $params['printers'],
'discount' => $discount,
'transfer' => $transfer,
'printOptions' => $printOptions,
'note' => $note,
'showPercentDiscount' => $doc->getDiscountType() === 'percent',
'discountPercent' => $doc->getDiscountPercent()
]);
}
$pdfPid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$html,
false,
$printOptions['paper']
);
// اگر چاپ ابری انتخاب شده، فایل را به صف پرینترها اضافه کن
if (!empty($params['printers'])) {
$printers->addFile($pdfPid, $acc, "fastSellInvoice");
}
}
if ($params['posPrint'] == true) {
$pid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$this->renderView('pdf/posPrinters/justSell.html.twig', [
'bid' => $acc['bid'],
'doc' => $doc,
'rows' => array_map(function ($row) {
return [
'commodity' => $row->getCommodity(),
'commodityCount' => $row->getCommdityCount(),
'des' => $row->getDes(),
'bs' => $row->getBs(),
'tax' => $row->getTax(),
'discount' => $row->getDiscount(),
'showPercentDiscount' => $row->getDiscountType() === 'percent',
'discountPercent' => $row->getDiscountPercent()
];
}, $doc->getHesabdariRows()->toArray()),
'discount' => $discount,
'showPercentDiscount' => $doc->getDiscountType() === 'percent',
'discountPercent' => $doc->getDiscountPercent()
]),
true
);
$printers->addFile($pid, $acc, "fastSellPosInvoice");
}
// چاپ قبض صندوق در صورت نیاز
if (!empty($params['posPrintRecp'])) {
$pid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$this->renderView('pdf/posPrinters/cashdesk.html.twig', [
'bid' => $acc['bid'],
'doc' => $doc,
'rows' => $doc->getHesabdariRows(),
]),
true
);
$printers->addFile($pid, $acc, "fastSellCashdesk");
}
return $this->json(['id' => $pdfPid]);
}
#[Route('/api/sell/chart/data', name: 'app_sell_chart_data')]
public function app_sell_chart_data(Jdate $jdate, Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
// create data numbers
$dayTime = 3600 * 24;
$dayNames = [];
$daySells = [];
for ($i = 0; $i < 7; $i++) {
$dayInfo = [
$jdate->jdate('l', time() - ($i * $dayTime)),
$jdate->jdate('Y/n/d', time() - ($i * $dayTime))
];
$dayNames[] = $jdate->jdate('l', time() - ($i * $dayTime));
//get sell docs
$docs = $entityManager->getRepository(HesabdariDoc::class)->findBy([
'bid' => $acc['bid'],
'money' => $acc['money'],
'year' => $acc['year'],
'type' => 'sell',
'date' => $dayInfo[1],
]);
$bd = 0;
foreach ($docs as $doc) {
foreach ($doc->getHesabdariRows() as $row) {
if ($row->getPerson()) {
$bd += $row->getBd();
}
}
}
$daySells[] = $bd;
}
return $this->json([
'dayNames' => $dayNames,
'daySells' => $daySells
]);
}
#[Route('/api/sell/v2/mod', name: 'app_sell_v2_mod', methods: ['POST'])]
public function app_sell_v2_mod(
AccountingPermissionService $accountingPermissionService,
PluginService $pluginService,
SMS $SMS,
Provider $provider,
Extractor $extractor,
Request $request,
Access $access,
Log $log,
EntityManagerInterface $entityManager,
registryMGR $registryMGR,
Jdate $jdate
): JsonResponse {
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
$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']
]);
}
$business = $entityManager->getRepository(Business::class)->find($acc['bid']);
$TwoStepApproval = $business && method_exists($business, 'isRequireTwoStepApproval') ? (bool)$business->isRequireTwoStepApproval() : false;
try {
// بررسی وجود فاکتور برای ویرایش
if (!empty($params['id'])) {
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'year' => $acc['year'],
'code' => $params['id'],
'money' => $acc['money']
]);
if (!$doc) {
return $this->json($extractor->notFound());
}
// حذف سطرهای قبلی
$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);
}
$relogs = $entityManager->getRepository(\App\Entity\Log::class)->findBy(['doc' => $relatedDoc, 'bid' => $acc['bid']]);
foreach ($relogs as $relog) {
$relog->setDoc(null);
$entityManager->persist($relog);
$entityManager->flush();
}
$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'));
if ($TwoStepApproval) {
$doc->setIsPreview(true);
$doc->setIsApproved(false);
$doc->setApprovedBy(null);
} else {
$doc->setIsPreview(false);
$doc->setIsApproved(true);
$doc->setApprovedBy($this->getUser());
}
}
// تنظیم اطلاعات اصلی فاکتور
if (isset($params['invoiceDescription'])) {
$doc->setDes($params['invoiceDescription']);
} else {
$doc->setDes('');
}
if (isset($params['invoiceDate'])) {
$doc->setDate($params['invoiceDate']);
} else {
$doc->setDate($jdate->jdate('Y/n/d', time()));
}
if (isset($params['taxPercent'])) {
$doc->setTaxPercent($params['taxPercent'] ?? 0);
}
// افزودن هزینه حمل
if (isset($params['shippingCost']) && $params['shippingCost'] > 0) {
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('حمل و نقل کالا');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs($params['shippingCost']);
$hesabdariRow->setBd(0);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '61']);
$hesabdariRow->setRef($ref);
$entityManager->persist($hesabdariRow);
}
// افزودن تخفیف کلی
$totalDiscount = 0;
if (isset($params['discountType']) && $params['discountType'] === 'percent') {
$totalDiscount = round(($params['totalInvoice'] * $params['discountPercent']) / 100);
$doc->setDiscountType('percent');
$doc->setDiscountPercent((float) $params['discountPercent']);
} else {
if (isset($params['totalDiscount']) && $params['totalDiscount'] > 0) {
$totalDiscount = $params['totalDiscount'];
} else {
$totalDiscount = 0;
}
$doc->setDiscountType('fixed');
$doc->setDiscountPercent(null);
}
if (isset($totalDiscount) && $totalDiscount > 0) {
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('تخفیف فاکتور');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs(0);
$hesabdariRow->setBd($totalDiscount);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '104']);
$hesabdariRow->setRef($ref);
$entityManager->persist($hesabdariRow);
}
// افزودن اقلام فاکتور
$sumTax = 0;
$sumTotal = 0;
foreach ($params['items'] as $item) {
$itemTotal = $item['total'] ?? 0;
$itemTax = $item['tax'] ?? 0;
$sumTotal += $itemTotal;
$sumTax += $itemTax;
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes($item['description'] ?? '');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs($itemTotal); // فقط مبلغ کالا بدون مالیات
$hesabdariRow->setBd(0);
$hesabdariRow->setDiscount($item['discountAmount'] ?? 0);
$hesabdariRow->setTax($itemTax);
$hesabdariRow->setDiscountType($item['showPercentDiscount'] ? 'percent' : 'fixed');
$hesabdariRow->setDiscountPercent($item['discountPercent'] ?? 0);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '53']);
$hesabdariRow->setRef($ref);
$commodity = $entityManager->getRepository(Commodity::class)->findOneBy([
'id' => $item['name']['id'],
'bid' => $acc['bid']
]);
if (!$commodity) {
throw new \Exception('کالا یافت نشد');
}
$hesabdariRow->setCommodity($commodity);
$hesabdariRow->setCommdityCount($item['count']);
// به‌روزرسانی قیمت فروش کالا اگر تنظیم شده باشد
if ($acc['bid']->isCommodityUpdateSellPriceAuto() && $commodity->getPriceSell() != $item['price']) {
$commodity->setPriceSell($item['price']);
$entityManager->persist($commodity);
}
$entityManager->persist($hesabdariRow);
}
// افزودن ردیف مالیات
if ($sumTax > 0) {
$taxRow = new HesabdariRow();
$taxRow->setDes('مالیات بر ارزش افزوده');
$taxRow->setBid($acc['bid']);
$taxRow->setYear($acc['year']);
$taxRow->setDoc($doc);
$taxRow->setBs($sumTax);
$taxRow->setBd(0);
$taxRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '33']);
$taxRow->setRef($taxRef);
$entityManager->persist($taxRow);
}
// تنظیم مبلغ کل فاکتور
$doc->setAmount($sumTotal + $sumTax - $totalDiscount + $params['shippingCost']);
// افزودن سطر اصلی فاکتور
$hesabdariRow = new HesabdariRow();
$hesabdariRow->setDes('فاکتور فروش');
$hesabdariRow->setBid($acc['bid']);
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs(0);
$hesabdariRow->setBd($sumTotal + $sumTax + $params['shippingCost'] - $totalDiscount);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
$hesabdariRow->setRef($ref);
if (!isset($params['customer']) || $params['customer'] == '') {
$person = $entityManager->getRepository(Person::class)->findOneBy([
'bid' => $acc['bid'],
'nikename' => 'مشتری پیش فرض'
]);
if (!$person) {
$person = new Person();
$person->setBid($acc['bid']);
$person->setNikename('مشتری پیش فرض');
$person->setCode($provider->getAccountingCode($acc['bid'], 'person'));
$entityManager->persist($person);
}
} else {
$person = $entityManager->getRepository(Person::class)->findOneBy([
'bid' => $acc['bid'],
'id' => $params['customer']
]);
}
if (!$person) {
throw new \Exception('خریدار یافت نشد');
}
$hesabdariRow->setPerson($person);
$entityManager->persist($hesabdariRow);
// ذخیره فاکتور
$entityManager->persist($doc);
$entityManager->flush();
// ایجاد لینک کوتاه اگر وجود نداشته باشد
if (!$doc->getShortlink()) {
$doc->setShortlink($provider->RandomString(8));
$entityManager->persist($doc);
$entityManager->flush();
}
// ثبت اسناد پرداخت
if (!empty($params['payments'])) {
foreach ($params['payments'] as $payment) {
// ایجاد سند حسابداری جدید برای پرداخت
$paymentDoc = new HesabdariDoc();
$paymentDoc->setBid($acc['bid']);
$paymentDoc->setYear($acc['year']);
$paymentDoc->setDateSubmit(time());
$paymentDoc->setType('sell_receive');
$paymentDoc->setSubmitter($this->getUser());
$paymentDoc->setMoney($acc['money']);
$paymentDoc->setCode($provider->getAccountingCode($acc['bid'], 'accounting'));
$paymentDoc->setDate($params['invoiceDate']);
$paymentDoc->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
$paymentDoc->setAmount($payment['amount']);
if ($TwoStepApproval) {
$paymentDoc->setIsPreview(true);
$paymentDoc->setIsApproved(false);
$paymentDoc->setApprovedBy(null);
} else {
$paymentDoc->setIsPreview(false);
$paymentDoc->setIsApproved(true);
$paymentDoc->setApprovedBy($this->getUser());
}
// ایجاد ارتباط با فاکتور اصلی
$doc->addRelatedDoc($paymentDoc);
// ایجاد سطرهای حسابداری بر اساس نوع پرداخت
if ($payment['type'] === 'bank') {
// دریافت از طریق حساب بانکی
$bankRow = new HesabdariRow();
$bankRow->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
$bankRow->setBid($acc['bid']);
$bankRow->setYear($acc['year']);
$bankRow->setDoc($paymentDoc);
$bankRow->setBs(0);
$bankRow->setBd($payment['amount']);
$bankRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '5']);
$bankRow->setRef($bankRef);
$bankRow->setBank($entityManager->getRepository(BankAccount::class)->find($payment['bank']));
$entityManager->persist($bankRow);
} elseif ($payment['type'] === 'cashdesk') {
// دریافت از طریق صندوق
$cashdeskRow = new HesabdariRow();
$cashdeskRow->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
$cashdeskRow->setBid($acc['bid']);
$cashdeskRow->setYear($acc['year']);
$cashdeskRow->setDoc($paymentDoc);
$cashdeskRow->setBs(0);
$cashdeskRow->setBd($payment['amount']);
$cashdeskRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '121']);
$cashdeskRow->setRef($cashdeskRef);
$cashdeskRow->setCashdesk($entityManager->getRepository(Cashdesk::class)->find($payment['cashdesk']));
$entityManager->persist($cashdeskRow);
} elseif ($payment['type'] === 'salary') {
// دریافت از طریق تنخواه گردان
$salaryRow = new HesabdariRow();
$salaryRow->setDes($payment['description'] ?? 'دریافت وجه فاکتور فروش شماره ' . $doc->getCode());
$salaryRow->setBid($acc['bid']);
$salaryRow->setYear($acc['year']);
$salaryRow->setDoc($paymentDoc);
$salaryRow->setBs(0);
$salaryRow->setBd($payment['amount']);
$salaryRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '122']);
$salaryRow->setRef($salaryRef);
$salaryRow->setSalary($entityManager->getRepository(Salary::class)->find($payment['salary']));
$entityManager->persist($salaryRow);
}
// ایجاد سطر دریافت از مشتری
$receiveRow = new HesabdariRow();
$receiveRow->setDes($payment['description'] ?? 'پرداخت وجه فاکتور فروش شماره ' . $doc->getCode());
$receiveRow->setBid($acc['bid']);
$receiveRow->setYear($acc['year']);
$receiveRow->setDoc($paymentDoc);
$receiveRow->setBs($payment['amount']);
$receiveRow->setBd(0);
$receiveRef = $entityManager->getRepository(HesabdariTable::class)->findOneBy(['code' => '3']);
$receiveRow->setRef($receiveRef);
$receiveRow->setPerson($person);
$entityManager->persist($receiveRow);
$entityManager->persist($paymentDoc);
}
$entityManager->flush();
}
// ثبت لاگ
$log->insert(
'حسابداری',
'سند حسابداری شماره ' . $doc->getCode() . ' ثبت / ویرایش شد.',
$this->getUser(),
$request->headers->get('activeBid'),
$doc
);
// ارسال پیامک اگر درخواست شده باشد
if (!empty($params['sendSmsToCustomer']) && $params['sendSmsToCustomer']) {
if ($pluginService->isActive('accpro', $acc['bid']) && $person->getMobile() != '' && $acc['bid']->getTel()) {
$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 {
$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([
'Success' => 1,
'result' => 1,
'message' => 'فاکتور با موفقیت ثبت شد',
'data' => [
'id' => $doc->getCode(),
'code' => $doc->getCode(),
'shortlink' => $doc->getShortlink()
]
]);
} catch (\Exception $e) {
return $this->json([
'result' => 0,
'message' => $e->getMessage(),
'data' => $params
]);
}
}
#[Route('/api/sell/v2/get/{id}', name: 'app_sell_v2_get', methods: ['GET'])]
public function app_sell_v2_get(
Request $request,
Access $access,
EntityManagerInterface $entityManager,
string $id
): JsonResponse {
try {
$acc = $access->hasRole('sell');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneByIncludePreview([
'bid' => $acc['bid'],
'year' => $acc['year'],
'code' => $id,
'money' => $acc['money']
]);
if (!$doc) {
throw $this->createNotFoundException('فاکتور یافت نشد');
}
$person = null;
$discountAll = 0;
$transferCost = 0;
$items = [];
$totalInvoice = 0;
$taxPercent = $doc->getTaxPercent();
$discountType = $doc->getDiscountType() ?? 'fixed';
$discountPercent = $doc->getDiscountPercent() ?? 0;
$payments = [];
// دریافت اسناد پرداخت مرتبط
$relatedDocs = $doc->getRelatedDocs();
foreach ($relatedDocs as $relatedDoc) {
if ($relatedDoc->getType() === 'sell_receive') {
$payment = [
'type' => null,
'amount' => $relatedDoc->getAmount(),
'reference' => '',
'description' => $relatedDoc->getDes(),
'bank' => null,
'cashdesk' => null,
'salary' => null
];
foreach ($relatedDoc->getHesabdariRows() as $row) {
if ($row->getBank()) {
$payment['type'] = 'bank';
$payment['bank'] = $row->getBank()->getId();
} elseif ($row->getCashdesk()) {
$payment['type'] = 'cashdesk';
$payment['cashdesk'] = $row->getCashdesk()->getId();
} elseif ($row->getSalary()) {
$payment['type'] = 'salary';
$payment['salary'] = $row->getSalary()->getId();
}
}
$payments[] = $payment;
}
}
foreach ($doc->getHesabdariRows() as $row) {
if ($row->getPerson()) {
$person = $row->getPerson();
} elseif ($row->getRef() && $row->getRef()->getCode() == '104') {
$discountAll = $row->getBd();
} elseif ($row->getRef() && $row->getRef()->getCode() == '61') {
$transferCost = $row->getBs();
} elseif ($row->getCommodity()) {
$basePrice = $row->getBs();
$itemDiscount = $row->getDiscount() ?? 0;
$itemDiscountType = $row->getDiscountType() ?? 'fixed';
$itemDiscountPercent = $row->getDiscountPercent() ?? 0;
$itemTax = $row->getTax() ?? 0;
// محاسبه قیمت واحد و تخفیف
if ($itemDiscountType === 'percent' && $itemDiscountPercent > 0) {
// محاسبه قیمت اصلی در حالت تخفیف درصدی
$originalPrice = $basePrice / (1 - ($itemDiscountPercent / 100));
$itemDiscount = round(($originalPrice * $itemDiscountPercent) / 100);
} else {
// محاسبه قیمت اصلی در حالت تخفیف مقداری
$originalPrice = $basePrice + $itemDiscount;
}
// محاسبه قیمت واحد
$unitPrice = $row->getCommdityCount() > 0 ? $originalPrice / $row->getCommdityCount() : 0;
// محاسبه قیمت خالص (بدون مالیات)
$netPrice = $basePrice;
$totalInvoice += $netPrice;
$items[] = [
'name' => [
'id' => $row->getCommodity()->getId(),
'name' => $row->getCommodity()->getName(),
'code' => $row->getCommodity()->getCode()
],
'count' => $row->getCommdityCount(),
'price' => $unitPrice,
'discountPercent' => $itemDiscountPercent,
'discountAmount' => $itemDiscount,
'total' => $netPrice,
'description' => $row->getDes(),
'showPercentDiscount' => $itemDiscountType === 'percent',
'tax' => $itemTax
];
}
}
// محاسبه تخفیف کلی از HesabdariDoc
$totalDiscount = 0;
if ($discountType === 'percent') {
$totalDiscount = round(($totalInvoice * $discountPercent) / 100);
} else {
$totalDiscount = $discountAll;
}
// محاسبه مبلغ نهایی با در نظر گرفتن تخفیف کلی و مالیات
$finalTotal = $totalInvoice - $totalDiscount + $transferCost;
$totalTax = 0;
foreach ($items as $item) {
$totalTax += $item['tax'];
}
$finalTotal += $totalTax;
return $this->json([
'result' => 1,
'data' => [
'id' => $doc->getCode(),
'date' => $doc->getDate(),
'person' => $person ? [
'id' => $person->getId(),
'name' => $person->getNikename(),
'code' => $person->getCode()
] : null,
'des' => $doc->getDes(),
'totalInvoice' => $totalInvoice,
'taxPercent' => $taxPercent,
'discountType' => $discountType,
'discountPercent' => $discountPercent,
'totalDiscount' => $totalDiscount,
'shippingCost' => $transferCost,
'showTotalPercentDiscount' => $discountType === 'percent',
'items' => $items,
'finalTotal' => $finalTotal,
'payments' => $payments
]
]);
} catch (\Exception $e) {
return $this->json([
'result' => 0,
'message' => $e->getMessage()
]);
}
}
#[Route('/api/sell/list/excel', name: 'app_sell_list_excel', methods: ['POST'])]
public function app_sell_list_excel(
Provider $provider,
Request $request,
Access $access,
Log $log,
EntityManagerInterface $entityManager,
Jdate $jdate
): BinaryFileResponse|JsonResponse|StreamedResponse {
$acc = $access->hasRole('sell');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$params = json_decode($request->getContent(), true) ?? [];
$searchTerm = $params['search'] ?? '';
$types = $params['types'] ?? [];
$dateFilter = $params['dateFilter'] ?? 'all';
$sortBy = $params['sortBy'] ?? [];
$queryBuilder = $entityManager->createQueryBuilder()
->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount')
->addSelect('d.isPreview, d.isApproved')
->addSelect('u.fullName as submitter')
->addSelect('approver.fullName as approvedByName, approver.id as approvedById, approver.email as approvedByEmail')
->addSelect('l.code as labelCode, l.label as labelLabel')
->from(HesabdariDoc::class, 'd')
->leftJoin('d.submitter', 'u')
->leftJoin('d.approvedBy', 'approver')
->leftJoin('d.InvoiceLabel', 'l')
->leftJoin('d.hesabdariRows', 'r')
->where('d.bid = :bid')
->andWhere('d.year = :year')
->andWhere('d.type = :type')
->andWhere('d.money = :money')
->setParameter('bid', $acc['bid'])
->setParameter('year', $acc['year'])
->setParameter('type', 'sell')
->setParameter('money', $acc['money']);
// اعمال فیلترهای تاریخ
$today = $jdate->jdate('Y/m/d', time());
if ($dateFilter === 'today') {
$queryBuilder->andWhere('d.date = :today')
->setParameter('today', $today);
} elseif ($dateFilter === 'week') {
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
->setParameter('weekStart', $weekStart)
->setParameter('today', $today);
} elseif ($dateFilter === 'month') {
$monthStart = $jdate->jdate('Y/m/01', time());
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
->setParameter('monthStart', $monthStart)
->setParameter('today', $today);
}
if ($searchTerm) {
$queryBuilder->leftJoin('r.person', 'p')
->andWhere(
$queryBuilder->expr()->orX(
'd.code LIKE :search',
'd.des LIKE :search',
'd.date LIKE :search',
'd.amount LIKE :search',
'p.nikename LIKE :search',
'p.mobile LIKE :search'
)
)
->setParameter('search', "%$searchTerm%");
}
if (!empty($types)) {
$queryBuilder->andWhere('l.code IN (:types)')
->setParameter('types', $types);
}
// اگر آیتم‌های خاصی درخواست شده‌اند
if (array_key_exists('items', $params)) {
$codes = array_map(function($item) { return $item['code']; }, $params['items']);
$queryBuilder->andWhere('d.code IN (:codes)')
->setParameter('codes', $codes);
}
// اعمال مرتب‌سازی
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
if ($key === 'profit' || $key === 'receivedAmount') {
continue; // این‌ها توی PHP مرتب می‌شن
} elseif (in_array($key, ['id', 'dateSubmit', 'date', 'type', 'code', 'des', 'amount', 'isPreview', 'isApproved'])) {
$queryBuilder->addOrderBy('d.' . $key, $direction);
} elseif ($key === 'submitter') {
$queryBuilder->addOrderBy('u.fullName', $direction);
} elseif ($key === 'label') {
$queryBuilder->addOrderBy('l.label', $direction);
}
}
} else {
$queryBuilder->orderBy('d.id', 'DESC');
}
$docs = $queryBuilder->getQuery()->getArrayResult();
$dataTemp = [];
foreach ($docs as $doc) {
$item = [
'id' => $doc['id'],
'dateSubmit' => $doc['dateSubmit'],
'date' => $doc['date'],
'type' => $doc['type'],
'code' => $doc['code'],
'des' => $doc['des'],
'amount' => $doc['amount'],
'submitter' => $doc['submitter'],
'label' => $doc['labelCode'] ? [
'code' => $doc['labelCode'],
'label' => $doc['labelLabel']
] : null,
'isPreview' => $doc['isPreview'],
'isApproved' => $doc['isApproved'],
'approvedBy' => $doc['approvedByName'] ? [
'fullName' => $doc['approvedByName'],
'id' => $doc['approvedById'],
'email' => $doc['approvedByEmail']
] : null,
];
$mainRow = $entityManager->getRepository(HesabdariRow::class)
->createQueryBuilder('r')
->where('r.doc = :docId')
->andWhere('r.person IS NOT NULL')
->setParameter('docId', $doc['id'])
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$item['person'] = $mainRow && $mainRow->getPerson() ? [
'id' => $mainRow->getPerson()->getId(),
'nikename' => $mainRow->getPerson()->getNikename(),
'code' => $mainRow->getPerson()->getCode()
] : null;
// استفاده از SQL خام برای محاسبه پرداختی‌ها
$sql = "
SELECT SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs
FROM hesabdari_doc rd
JOIN hesabdari_doc_hesabdari_doc rel ON rel.hesabdari_doc_target = rd.id
WHERE rel.hesabdari_doc_source = :sourceDocId
AND rd.bid_id = :bidId
";
$stmt = $entityManager->getConnection()->prepare($sql);
$stmt->bindValue('sourceDocId', $doc['id']);
$stmt->bindValue('bidId', $acc['bid']->getId());
$result = $stmt->executeQuery()->fetchAssociative();
$relatedDocsPays = $result['total_pays'] ?? 0;
$relatedDocsCount = $result['count_docs'] ?? 0;
$item['relatedDocsCount'] = (int) $relatedDocsCount;
$item['relatedDocsPays'] = $relatedDocsPays;
$item['profit'] = $this->calculateProfit($doc['id'], $acc, $entityManager);
$item['discountAll'] = 0;
$item['transferCost'] = 0;
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
foreach ($rows as $row) {
if ($row->getRef()->getCode() == '104') {
$item['discountAll'] = $row->getBd();
} elseif ($row->getRef()->getCode() == '61') {
$item['transferCost'] = $row->getBs();
}
}
$dataTemp[] = $item;
}
// مرتب‌سازی توی PHP برای profit و receivedAmount
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? SORT_DESC : SORT_ASC;
if ($key === 'profit') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['profit'] - $b['profit'] : $b['profit'] - $a['profit'];
});
} elseif ($key === 'receivedAmount') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['relatedDocsPays'] - $b['relatedDocsPays'] : $b['relatedDocsPays'] - $a['relatedDocsPays'];
});
}
}
}
// آماده‌سازی داده‌ها برای Excel
$excelData = [];
$headers = [
'ردیف',
'کد فاکتور',
'تاریخ',
'خریدار',
'وضعیت تایید',
'تاییدکننده',
'تخفیف',
'حمل و نقل',
'مبلغ',
'سود فاکتور',
'پرداختی',
'برچسب',
'شرح'
];
$excelData[] = $headers;
foreach ($dataTemp as $index => $item) {
$excelData[] = [
$index + 1,
$item['code'],
$item['date'],
$item['person'] ? $item['person']['nikename'] : '-',
$item['isApproved'] ? 'تایید شده' : ($item['isPreview'] ? 'در انتظار تایید' : 'تایید شده'),
$item['approvedBy'] ? $item['approvedBy']['fullName'] : '-',
number_format($item['discountAll']),
number_format($item['transferCost']),
number_format($item['amount']),
number_format($item['profit']),
number_format($item['relatedDocsPays']),
$item['label'] ? $item['label']['label'] : '-',
$item['des']
];
}
return new BinaryFileResponse($provider->createExcellFromArray($excelData));
}
#[Route('/api/sell/list/pdf', name: 'app_sell_list_pdf', methods: ['POST'])]
public function app_sell_list_pdf(
Provider $provider,
Request $request,
Access $access,
Log $log,
EntityManagerInterface $entityManager,
Jdate $jdate
): JsonResponse {
$acc = $access->hasRole('sell');
if (!$acc) {
throw $this->createAccessDeniedException();
}
$params = json_decode($request->getContent(), true) ?? [];
$searchTerm = $params['search'] ?? '';
$types = $params['types'] ?? [];
$dateFilter = $params['dateFilter'] ?? 'all';
$sortBy = $params['sortBy'] ?? [];
$queryBuilder = $entityManager->createQueryBuilder()
->select('DISTINCT d.id, d.dateSubmit, d.date, d.type, d.code, d.des, d.amount')
->addSelect('d.isPreview, d.isApproved')
->addSelect('u.fullName as submitter')
->addSelect('approver.fullName as approvedByName, approver.id as approvedById, approver.email as approvedByEmail')
->addSelect('l.code as labelCode, l.label as labelLabel')
->from(HesabdariDoc::class, 'd')
->leftJoin('d.submitter', 'u')
->leftJoin('d.approvedBy', 'approver')
->leftJoin('d.InvoiceLabel', 'l')
->leftJoin('d.hesabdariRows', 'r')
->where('d.bid = :bid')
->andWhere('d.year = :year')
->andWhere('d.type = :type')
->andWhere('d.money = :money')
->setParameter('bid', $acc['bid'])
->setParameter('year', $acc['year'])
->setParameter('type', 'sell')
->setParameter('money', $acc['money']);
// اعمال فیلترهای تاریخ
$today = $jdate->jdate('Y/m/d', time());
if ($dateFilter === 'today') {
$queryBuilder->andWhere('d.date = :today')
->setParameter('today', $today);
} elseif ($dateFilter === 'week') {
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
->setParameter('weekStart', $weekStart)
->setParameter('today', $today);
} elseif ($dateFilter === 'month') {
$monthStart = $jdate->jdate('Y/m/01', time());
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
->setParameter('monthStart', $monthStart)
->setParameter('today', $today);
}
if ($searchTerm) {
$queryBuilder->leftJoin('r.person', 'p')
->andWhere(
$queryBuilder->expr()->orX(
'd.code LIKE :search',
'd.des LIKE :search',
'd.date LIKE :search',
'd.amount LIKE :search',
'p.nikename LIKE :search',
'p.mobile LIKE :search'
)
)
->setParameter('search', "%$searchTerm%");
}
if (!empty($types)) {
$queryBuilder->andWhere('l.code IN (:types)')
->setParameter('types', $types);
}
// اگر آیتم‌های خاصی درخواست شده‌اند
if (array_key_exists('items', $params)) {
$codes = array_map(function($item) { return $item['code']; }, $params['items']);
$queryBuilder->andWhere('d.code IN (:codes)')
->setParameter('codes', $codes);
}
// اعمال مرتب‌سازی
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? 'DESC' : 'ASC';
if ($key === 'profit' || $key === 'receivedAmount') {
continue; // این‌ها توی PHP مرتب می‌شن
} elseif (in_array($key, ['id', 'dateSubmit', 'date', 'type', 'code', 'des', 'amount', 'isPreview', 'isApproved'])) {
$queryBuilder->addOrderBy('d.' . $key, $direction);
} elseif ($key === 'submitter') {
$queryBuilder->addOrderBy('u.fullName', $direction);
} elseif ($key === 'label') {
$queryBuilder->addOrderBy('l.label', $direction);
}
}
} else {
$queryBuilder->orderBy('d.id', 'DESC');
}
$docs = $queryBuilder->getQuery()->getArrayResult();
$dataTemp = [];
foreach ($docs as $doc) {
$item = [
'id' => $doc['id'],
'dateSubmit' => $doc['dateSubmit'],
'date' => $doc['date'],
'type' => $doc['type'],
'code' => $doc['code'],
'des' => $doc['des'],
'amount' => $doc['amount'],
'submitter' => $doc['submitter'],
'label' => $doc['labelCode'] ? [
'code' => $doc['labelCode'],
'label' => $doc['labelLabel']
] : null,
'isPreview' => $doc['isPreview'],
'isApproved' => $doc['isApproved'],
'approvedBy' => $doc['approvedByName'] ? [
'fullName' => $doc['approvedByName'],
'id' => $doc['approvedById'],
'email' => $doc['approvedByEmail']
] : null,
];
$mainRow = $entityManager->getRepository(HesabdariRow::class)
->createQueryBuilder('r')
->where('r.doc = :docId')
->andWhere('r.person IS NOT NULL')
->setParameter('docId', $doc['id'])
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$item['person'] = $mainRow && $mainRow->getPerson() ? [
'id' => $mainRow->getPerson()->getId(),
'nikename' => $mainRow->getPerson()->getNikename(),
'code' => $mainRow->getPerson()->getCode()
] : null;
// استفاده از SQL خام برای محاسبه پرداختی‌ها
$sql = "
SELECT SUM(rd.amount) as total_pays, COUNT(rd.id) as count_docs
FROM hesabdari_doc rd
JOIN hesabdari_doc_hesabdari_doc rel ON rel.hesabdari_doc_target = rd.id
WHERE rel.hesabdari_doc_source = :sourceDocId
AND rd.bid_id = :bidId
";
$stmt = $entityManager->getConnection()->prepare($sql);
$stmt->bindValue('sourceDocId', $doc['id']);
$stmt->bindValue('bidId', $acc['bid']->getId());
$result = $stmt->executeQuery()->fetchAssociative();
$relatedDocsPays = $result['total_pays'] ?? 0;
$relatedDocsCount = $result['count_docs'] ?? 0;
$item['relatedDocsCount'] = (int) $relatedDocsCount;
$item['relatedDocsPays'] = $relatedDocsPays;
$item['profit'] = $this->calculateProfit($doc['id'], $acc, $entityManager);
$item['discountAll'] = 0;
$item['transferCost'] = 0;
$rows = $entityManager->getRepository(HesabdariRow::class)->findBy(['doc' => $doc]);
foreach ($rows as $row) {
if ($row->getRef()->getCode() == '104') {
$item['discountAll'] = $row->getBd();
} elseif ($row->getRef()->getCode() == '61') {
$item['transferCost'] = $row->getBs();
}
}
$dataTemp[] = $item;
}
// مرتب‌سازی توی PHP برای profit و receivedAmount
if (!empty($sortBy)) {
foreach ($sortBy as $sort) {
$key = $sort['key'] ?? 'id';
$direction = isset($sort['order']) && strtoupper($sort['order']) === 'DESC' ? SORT_DESC : SORT_ASC;
if ($key === 'profit') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['profit'] - $b['profit'] : $b['profit'] - $a['profit'];
});
} elseif ($key === 'receivedAmount') {
usort($dataTemp, function ($a, $b) use ($direction) {
return $direction === SORT_ASC ? $a['relatedDocsPays'] - $b['relatedDocsPays'] : $b['relatedDocsPays'] - $a['relatedDocsPays'];
});
}
}
}
$html = $this->renderView('pdf/sell_list.html.twig', [
'items' => $dataTemp,
'bid' => $acc['bid'],
'currentPage' => 1,
'totalPages' => 1,
'totalItems' => count($dataTemp),
'page_title' => 'لیست فاکتورهای فروش'
]);
$pdfPid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$html,
false,
'A4-L'
);
return $this->json(['id' => $pdfPid]);
}
}