almost finish preinvoice and cheque parts

This commit is contained in:
Hesabix 2025-04-13 13:09:35 +00:00
parent 9bc3a27ce2
commit c630bc7dca
19 changed files with 2906 additions and 2514 deletions

View file

@ -92,3 +92,6 @@ services:
Printers: Printers:
class: App\Service\Printers class: App\Service\Printers
arguments: [ '@doctrine.orm.entity_manager' ] arguments: [ '@doctrine.orm.entity_manager' ]
App\Twig\NumberFormatExtension:
tags: ['twig.extension']

View file

@ -2,7 +2,6 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\PreInvoiceDoc;
use App\Service\Log; use App\Service\Log;
use App\Service\Access; use App\Service\Access;
use App\Service\Explore; use App\Service\Explore;
@ -10,8 +9,8 @@ use App\Entity\Commodity;
use App\Service\PluginService; use App\Service\PluginService;
use App\Service\Provider; use App\Service\Provider;
use App\Service\Extractor; use App\Service\Extractor;
use App\Entity\HesabdariDoc; use App\Entity\PreInvoiceDoc;
use App\Entity\HesabdariRow; use App\Entity\PreInvoiceItem;
use App\Entity\HesabdariTable; use App\Entity\HesabdariTable;
use App\Entity\InvoiceType; use App\Entity\InvoiceType;
use App\Entity\Person; use App\Entity\Person;
@ -29,524 +28,282 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class PreinvoiceController extends AbstractController class PreinvoiceController extends AbstractController
{ {
#[Route('/api/presell/get/info/{code}', name: 'app_sell_get_info')] private $access;
public function app_sell_get_info(Request $request, Access $access, Log $log, EntityManagerInterface $entityManager, string $code): JsonResponse private $extractor;
public function __construct(Access $access, Extractor $extractor)
{ {
$acc = $access->hasRole('sell'); $this->access = $access;
if (!$acc) $this->extractor = $extractor;
throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $code,
'money'=> $acc['money']
]);
if (!$doc)
throw $this->createNotFoundException();
$result = Explore::ExploreSellDoc($doc);
$profit = 0;
//calculate profit
foreach ($doc->getHesabdariRows() as $item) {
if ($item->getCommodity() && $item->getCommdityCount()) {
if ($acc['bid']->getProfitCalctype() == 'simple') {
$profit = $profit + (($item->getCommodity()->getPriceSell() - $item->getCommodity()->getPriceSell()) * $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();
}
}
//round output
$profit = round($profit);
}
}
$result['profit'] = $profit;
return $this->json($result);
} }
#[Route('/api/presell/mod', name: 'app_sell_mod')]
public function app_sell_mod(registryMGR $registryMGR, PluginService $pluginService, SMS $SMS, Provider $provider, Extractor $extractor, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse #[Route('/api/preinvoice/get/{id}', name: 'app_preinvoice_get')]
public function getPreinvoice(EntityManagerInterface $entityManager, int $id): JsonResponse
{ {
$params = []; $acc = $this->access->hasRole('preinvoice');
if ($content = $request->getContent()) { if (!$acc) {
$params = json_decode($content, true); return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
} }
$acc = $access->hasRole('sell'); $preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy(['code' => $id, 'bid' => $acc['bid'], 'year' => $acc['year']]);
if (!$acc) if (!$preinvoice) {
throw $this->createAccessDeniedException(); return new JsonResponse(['error' => 'پیش فاکتور یافت نشد'], 404);
if (!array_key_exists('update', $params)) {
return $this->json($extractor->paramsNotSend());
} }
if ($params['update'] != '') {
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([ $data = [
'bid' => $acc['bid'], 'id' => $preinvoice->getId(),
'year' => $acc['year'], 'code' => $preinvoice->getCode(),
'code' => $params['update'], 'date' => $preinvoice->getDate(),
'money'=> $acc['money'] 'des' => $preinvoice->getDes(),
]); 'person' => Explore::ExplorePerson($preinvoice->getPerson()),
if (!$doc) 'amount' => $preinvoice->getAmount(),
return $this->json($extractor->notFound()); 'taxPercent' => $preinvoice->getTaxPercent(),
'totalDiscount' => $preinvoice->getTotalDiscount(),
$rows = $doc->getHesabdariRows(); 'totalDiscountPercent' => $preinvoice->getTotalDiscountPercent(),
foreach ($rows as $row) 'shippingCost' => $preinvoice->getShippingCost(),
$entityManager->remove($row); 'showPercentDiscount' => $preinvoice->isShowPercentDiscount(),
} else { 'showTotalPercentDiscount' => $preinvoice->isShowTotalPercentDiscount(),
$doc = new HesabdariDoc(); 'items' => array_map(function($item) {
$doc->setBid($acc['bid']); return [
$doc->setYear($acc['year']); 'id' => $item->getId(),
$doc->setDateSubmit(time()); 'commodity' => [
$doc->setType('sell'); 'id' => $item->getCommodity()->getId(),
$doc->setSubmitter($this->getUser()); 'name' => $item->getCommodity()->getName()
$doc->setMoney($acc['money']); ],
$doc->setCode($provider->getAccountingCode($acc['bid'], 'accounting')); 'count' => $item->getCommodityCount(),
} 'price' => $item->getBs(),
if ($params['transferCost'] != 0) { 'discountPercent' => $item->getDiscountPercent(),
$hesabdariRow = new HesabdariRow(); 'discountAmount' => $item->getDiscountAmount(),
$hesabdariRow->setDes('حمل و نقل کالا'); 'description' => $item->getDes(),
$hesabdariRow->setBid($acc['bid']); 'showPercentDiscount' => $item->isShowPercentDiscount()
$hesabdariRow->setYear($acc['year']);
$hesabdariRow->setDoc($doc);
$hesabdariRow->setBs($params['transferCost']);
$hesabdariRow->setBd(0);
$ref = $entityManager->getRepository(HesabdariTable::class)->findOneBy([
'code' => '61' // transfer cost income
]);
$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->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' // sell commodity
]);
$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);
//update commodity price for auto update price option
if ($acc['bid']->isCommodityUpdateSellPriceAuto() == true && $commodity->getPriceSell() != $row['price']) {
$commodity->setPriceSell($row['price']);
$entityManager->persist($commodity);
}
}
//set amount of document
$doc->setAmount($sumTax + $sumTotal - $params['discountAll'] + $params['transferCost']);
//set person person
$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' // persons
]);
$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);
//set tax info
$entityManager->persist($doc);
$entityManager->flush();
if(!$doc->getShortlink()){
$doc->setShortlink($provider->RandomString(8));
}
//add pair docs
if(array_key_exists('pair_docs',$params)){
foreach($params['pair_docs'] as $pairCode){
$pair = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid'=>$acc['bid'],
'code'=>$pairCode,
'type'=>'buy'
]);
if($pair){
$doc->addPairDoc($pair);
}
}
}
$entityManager->persist($doc);
$entityManager->flush();
$log->insert(
'حسابداری',
'سند حسابداری شماره ' . $doc->getCode() . ' ثبت / ویرایش شد.',
$this->getUser(),
$request->headers->get('activeBid'),
$doc
);
//send sms to customer
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->getId(), $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->getId()],
$registryMGR->get('sms', 'sharefaktor'),
$person->getMobile(),
$acc['bid'],
$this->getUser(),
3
)
]);
}
}
}
return $this->json($extractor->operationSuccess());
}
#[Route('/api/presell/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/presell/docs/search', name: 'app_sell_docs_search')]
public function app_sell_docs_search(Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
{
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
$data = $entityManager->getRepository(HesabdariDoc::class)->findBy([
'bid' => $acc['bid'],
'year' => $acc['year'],
'type' => 'sell',
'money'=> $acc['money']
], [
'id' => 'DESC'
]);
$dataTemp = [];
foreach ($data as $item) {
$temp = [
'id' => $item->getId(),
'dateSubmit' => $item->getDateSubmit(),
'date' => $item->getDate(),
'type' => $item->getType(),
'code' => $item->getCode(),
'des' => $item->getDes(),
'amount' => $item->getAmount(),
'submitter' => $item->getSubmitter()->getFullName(),
];
$mainRow = $entityManager->getRepository(HesabdariRow::class)->getNotEqual($item, 'person');
$temp['person'] = '';
if ($mainRow)
$temp['person'] = Explore::ExplorePerson($mainRow->getPerson());
$temp['label'] = null;
if ($item->getInvoiceLabel()) {
$temp['label'] = [
'code' => $item->getInvoiceLabel()->getCode(),
'label' => $item->getInvoiceLabel()->getLabel()
]; ];
} }, $preinvoice->getPreInvoiceItems()->toArray())
];
$temp['relatedDocsCount'] = count($item->getRelatedDocs()); return new JsonResponse($data);
$pays = 0;
foreach ($item->getRelatedDocs() as $relatedDoc) {
$pays += $relatedDoc->getAmount();
}
$temp['relatedDocsPays'] = $pays;
// this variable is for store profit of invoice
$temp['profit'] = 0;
foreach ($item->getHesabdariRows() as $item) {
if ($item->getRef()->getCode() == '104') {
$temp['discountAll'] = $item->getBd();
} elseif ($item->getRef()->getCode() == '61') {
$temp['transferCost'] = $item->getBs();
}
//calculate profit
if ($item->getCommodity() && $item->getCommdityCount()) {
if ($acc['bid']->getProfitCalctype() == 'lis') {
$last = $entityManager->getRepository(HesabdariRow::class)->findOneBy([
'commodity' => $item->getCommodity(),
'bs' => 0
], [
'id' => 'DESC'
]);
if ($last) {
$price = $last->getBd() / $last->getCommdityCount();
$temp['profit'] = $temp['profit'] + ((($item->getBs() / $item->getCommdityCount()) - $price) * $item->getCommdityCount());
} else {
$temp['profit'] = $temp['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;
$temp['profit'] = $temp['profit'] + ((($item->getBs() / $item->getCommdityCount()) - $price) * $item->getCommdityCount());
}
else{
$temp['profit'] = $temp['profit'] + $item->getBs();
}
}
//round output
$temp['profit'] = round($temp['profit']);
}
}
if (!array_key_exists('discountAll', $temp))
$temp['discountAll'] = 0;
if (!array_key_exists('transferCost', $temp))
$temp['transferCost'] = 0;
$dataTemp[] = $temp;
}
return $this->json($dataTemp);
} }
#[Route('/api/presell/posprinter/invoice', name: 'app_sell_posprinter_invoice')] #[Route('/api/preinvoice/save', name: 'app_preinvoice_save')]
public function app_sell_posprinter_invoice(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse public function savePreinvoice(Access $access,Provider $provider, Log $log, EntityManagerInterface $entityManager, Request $request): JsonResponse
{
$acc = $access->hasRole('preinvoice');
if (!$acc) {
return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
}
$data = json_decode($request->getContent(), true);
if (isset($data['id']) && $data['id']) {
$preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy(['code' => $data['id'], 'bid' => $acc['bid'], 'year' => $acc['year']]);
if (!$preinvoice) {
return new JsonResponse(['error' => 'پیش فاکتور یافت نشد'], 404);
}
} else {
$preinvoice = new PreInvoiceDoc();
$preinvoice->setCode($this->generateCode($entityManager));
$preinvoice->setSubmitter($this->getUser());
$preinvoice->setYear($acc['year']);
$preinvoice->setBid($acc['bid']);
$preinvoice->setMoney($acc['money']);
$preinvoice->setStatus(1);
$preinvoice->setCode($provider->getAccountingCode($acc['bid'], 'accounting'));
}
$person = $entityManager->getRepository(Person::class)->find($data['person']);
if (!$person) {
return new JsonResponse(['error' => 'شخص یافت نشد'], 404);
}
$preinvoice->setPerson($person);
$preinvoice->setDate($data['date']);
$preinvoice->setDes($data['des']);
$preinvoice->setAmount($data['amount']);
$preinvoice->setTaxPercent($data['taxPercent']);
$preinvoice->setTotalDiscount($data['totalDiscount']);
$preinvoice->setTotalDiscountPercent($data['totalDiscountPercent']);
$preinvoice->setShippingCost($data['shippingCost']);
$preinvoice->setShowPercentDiscount($data['showPercentDiscount']);
$preinvoice->setShowTotalPercentDiscount($data['showTotalPercentDiscount']);
$entityManager->persist($preinvoice);
$entityManager->flush();
// حذف آیتم‌های قبلی
if (isset($data['id']) && $data['id']) {
foreach ($preinvoice->getPreInvoiceItems() as $oldItem) {
$entityManager->remove($oldItem);
}
}
// اضافه کردن آیتم‌های جدید
foreach ($data['items'] as $itemData) {
$item = new PreInvoiceItem();
$commodity = $entityManager->getRepository(Commodity::class)->find($itemData['commodity']);
if (!$commodity) {
continue;
}
$item->setCommodity($commodity);
$item->setCommodityCount($itemData['count']);
$item->setBs($itemData['price']);
$item->setDiscountPercent($itemData['discountPercent']);
$item->setDiscountAmount($itemData['discountAmount']);
$item->setDes($itemData['description']);
$item->setShowPercentDiscount($itemData['showPercentDiscount']);
$item->setDoc($preinvoice);
$entityManager->persist($item);
}
$entityManager->flush();
$log->insert(
'پیش فاکتور',
'پیش فاکتور شماره ' . $preinvoice->getCode() . ' ثبت / ویرایش شد.',
$this->getUser(),
$acc['bid'],
);
return new JsonResponse(['id' => $preinvoice->getId()]);
}
#[Route('/api/preinvoice/delete/{id}', name: 'app_preinvoice_delete')]
public function deletePreinvoice(Access $access, Log $log, EntityManagerInterface $entityManager, int $id): JsonResponse
{
$acc = $access->hasRole('preinvoice');
if (!$acc) {
return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
}
$preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy(['code' => $id, 'bid' => $acc['bid'], 'year' => $acc['year']] );
if (!$preinvoice) {
return new JsonResponse(['error' => 'پیش فاکتور یافت نشد'], 404);
}
foreach ($preinvoice->getPreInvoiceItems() as $item) {
$entityManager->remove($item);
}
$entityManager->remove($preinvoice);
$entityManager->flush();
$log->insert(
'پیش فاکتور',
'پیش فاکتور شماره ' . $preinvoice->getCode() . ' حذف شد.',
$this->getUser(),
$acc['bid'],
);
return new JsonResponse(['message' => 'پیش فاکتور با موفقیت حذف شد']);
}
private function generateCode(EntityManagerInterface $entityManager): string
{
$lastPreinvoice = $entityManager->getRepository(PreInvoiceDoc::class)
->findOneBy([], ['id' => 'DESC']);
$lastNumber = $lastPreinvoice ? (int)substr($lastPreinvoice->getCode(), 1) : 0;
return 'P' . str_pad($lastNumber + 1, 6, '0', STR_PAD_LEFT);
}
#[Route('/api/preinvoice/docs/search', name: 'app_presell_search')]
public function searchPreinvoices(Access $access, EntityManagerInterface $entityManager): JsonResponse
{
$acc = $access->hasRole('preinvoice');
if (!$acc) {
return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
}
$preinvoices = $entityManager->getRepository(PreInvoiceDoc::class)
->findBy(['bid' => $acc['bid'], 'year' => $acc['year']], ['id' => 'DESC']);
$result = [];
foreach ($preinvoices as $preinvoice) {
$totalAmount = $preinvoice->getAmount() - $preinvoice->getTotalDiscount() + $preinvoice->getShippingCost();
$result[] = [
'code' => $preinvoice->getCode(),
'date' => $preinvoice->getDate(),
'des' => $preinvoice->getDes(),
'person' => [
'code' => $preinvoice->getPerson()->getId(),
'nikename' => $preinvoice->getPerson()->getNikename()
],
'amount' => $preinvoice->getAmount(),
'discountAll' => $preinvoice->getTotalDiscount(),
'transferCost' => $preinvoice->getShippingCost(),
'totalAmount' => $totalAmount,
'label' => $preinvoice->getInvoiceLabel() ? [
'code' => $preinvoice->getInvoiceLabel()->getCode(),
'label' => $preinvoice->getInvoiceLabel()->getLabel()
] : null
];
}
return new JsonResponse($result);
}
#[Route('/api/preinvoice/remove/group', name: 'app_presell_delete_group')]
public function deletePreinvoiceGroup(Log $log, Access $access, EntityManagerInterface $entityManager, Request $request): JsonResponse
{
$acc = $access->hasRole('preinvoice');
if (!$acc) {
return new JsonResponse($this->extractor->operationFail('دسترسی ندارید'), 403);
}
$data = json_decode($request->getContent(), true);
$items = $data['items'] ?? [];
if (empty($items)) {
return new JsonResponse(['result' => 2, 'message' => 'هیچ موردی انتخاب نشده است']);
}
foreach ($items as $itemCode) {
$preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)
->findOneBy(['code' => $itemCode['code'], 'bid' => $acc['bid'], 'year' => $acc['year']]);
if ($preinvoice) {
// حذف آیتم‌های پیش فاکتور
foreach ($preinvoice->getPreInvoiceItems() as $item) {
$entityManager->remove($item);
}
$entityManager->remove($preinvoice);
}
$log->insert(
'پیش فاکتور',
'پیش فاکتور شماره ' . $preinvoice->getCode() . ' حذف شد.',
$this->getUser(),
$acc['bid'],
);
}
$entityManager->flush();
return new JsonResponse(['result' => 1, 'message' => 'پیش فاکتورها با موفقیت حذف شدند']);
}
#[Route('/api/preinvoice/print/invoice', name: 'app_preinvoice_print_invoice')]
public function printPreinvoice(Printers $printers, Provider $provider, Request $request, Access $access, Log $log, EntityManagerInterface $entityManager): JsonResponse
{ {
$params = []; $params = [];
if ($content = $request->getContent()) { if ($content = $request->getContent()) {
$params = json_decode($content, true); $params = json_decode($content, true);
} }
$acc = $access->hasRole('sell'); $acc = $access->hasRole('preinvoice');
if (!$acc) if (!$acc) {
throw $this->createAccessDeniedException(); throw $this->createAccessDeniedException();
}
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([ $preinvoice = $entityManager->getRepository(PreInvoiceDoc::class)->findOneBy([
'bid' => $acc['bid'], 'bid' => $acc['bid'],
'code' => $params['code'], 'code' => $params['code'],
'money'=> $acc['money'] 'year' => $acc['year']
]); ]);
if (!$doc)
if (!$preinvoice) {
throw $this->createNotFoundException(); throw $this->createNotFoundException();
$pdfPid = 0;
if ($params['pdf']) {
$pdfPid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$this->renderView('pdf/posPrinters/presell.html.twig', [
'bid' => $acc['bid'],
'doc' => $doc,
'rows' => $doc->getHesabdariRows(),
'printInvoice' => $params['posPrint'],
'printcashdeskRecp' => $params['posPrintRecp'],
]),
true
);
} }
if ($params['posPrint'] == true) {
$pid = $provider->createPrint(
$acc['bid'],
$this->getUser(),
$this->renderView('pdf/posPrinters/justSell.html.twig', [
'bid' => $acc['bid'],
'doc' => $doc,
'rows' => $doc->getHesabdariRows(),
]),
true
);
$printers->addFile($pid, $acc, "fastSellInvoice");
}
if ($params['posPrintRecp'] == true) {
$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/presell/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): JsonResponse
{
$params = [];
if ($content = $request->getContent()) {
$params = json_decode($content, true);
}
$acc = $access->hasRole('sell');
if (!$acc)
throw $this->createAccessDeniedException();
$doc = $entityManager->getRepository(HesabdariDoc::class)->findOneBy([
'bid' => $acc['bid'],
'code' => $params['code'],
'money'=> $acc['money']
]);
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; $pdfPid = 0;
if ($params['pdf']) { if ($params['pdf']) {
$printOptions = [ $printOptions = [
@ -557,6 +314,7 @@ class PreinvoiceController extends AbstractController
'note' => true, 'note' => true,
'paper' => 'A4-L' 'paper' => 'A4-L'
]; ];
if (array_key_exists('printOptions', $params)) { if (array_key_exists('printOptions', $params)) {
if (array_key_exists('bidInfo', $params['printOptions'])) { if (array_key_exists('bidInfo', $params['printOptions'])) {
$printOptions['bidInfo'] = $params['printOptions']['bidInfo']; $printOptions['bidInfo'] = $params['printOptions']['bidInfo'];
@ -577,22 +335,24 @@ class PreinvoiceController extends AbstractController
$printOptions['paper'] = $params['printOptions']['paper']; $printOptions['paper'] = $params['printOptions']['paper'];
} }
} }
$note = ''; $note = '';
$printSettings = $entityManager->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]); $printSettings = $entityManager->getRepository(PrintOptions::class)->findOneBy(['bid' => $acc['bid']]);
if ($printSettings) { if ($printSettings) {
$note = $printSettings->getSellNoteString(); $note = $printSettings->getSellNoteString();
} }
$pdfPid = $provider->createPrint( $pdfPid = $provider->createPrint(
$acc['bid'], $acc['bid'],
$this->getUser(), $this->getUser(),
$this->renderView('pdf/printers/presell.html.twig', [ $this->renderView('pdf/printers/preinvoice.html.twig', [
'bid' => $acc['bid'], 'bid' => $acc['bid'],
'doc' => $doc, 'doc' => $preinvoice,
'rows' => $doc->getHesabdariRows(), 'items' => $preinvoice->getPreInvoiceItems(),
'person' => $person, 'person' => $preinvoice->getPerson(),
'printInvoice' => $params['printers'], 'printInvoice' => $params['printers'],
'discount' => $discount, 'discount' => $preinvoice->getTotalDiscount(),
'transfer' => $transfer, 'transfer' => $preinvoice->getShippingCost(),
'printOptions' => $printOptions, 'printOptions' => $printOptions,
'note' => $note 'note' => $note
]), ]),
@ -600,19 +360,21 @@ class PreinvoiceController extends AbstractController
$printOptions['paper'] $printOptions['paper']
); );
} }
if ($params['printers'] == true) { if ($params['printers'] == true) {
$pid = $provider->createPrint( $pid = $provider->createPrint(
$acc['bid'], $acc['bid'],
$this->getUser(), $this->getUser(),
$this->renderView('pdf/posPrinters/justSell.html.twig', [ $this->renderView('pdf/posPrinters/justPreinvoice.html.twig', [
'bid' => $acc['bid'], 'bid' => $acc['bid'],
'doc' => $doc, 'doc' => $preinvoice,
'rows' => $doc->getHesabdariRows(), 'items' => $preinvoice->getPreInvoiceItems(),
]), ]),
false false
); );
$printers->addFile($pid, $acc, "fastSellInvoice"); $printers->addFile($pid, $acc, "fastPreinvoice");
} }
return $this->json(['id' => $pdfPid]); return $this->json(['id' => $pdfPid]);
} }
} }

View file

@ -18,6 +18,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Serializer\Annotation\Groups;
class YearController extends AbstractController class YearController extends AbstractController
{ {
@ -46,7 +47,7 @@ class YearController extends AbstractController
//no year created create first year //no year created create first year
$years = [$this->createDefaultYear($business,$entityManager)]; $years = [$this->createDefaultYear($business,$entityManager)];
} }
return $this->json($years); return $this->json($years, 200, [], ['groups' => ['year:read']]);
} }
#[Route('/api/year/get', name: 'app_year_get')] #[Route('/api/year/get', name: 'app_year_get')]
@ -65,7 +66,8 @@ class YearController extends AbstractController
$yearLoad->setStart($jdate->jdate('Y/m/d', $yearLoad->getStart())); $yearLoad->setStart($jdate->jdate('Y/m/d', $yearLoad->getStart()));
$yearLoad->setEnd($jdate->jdate('Y/m/d', $yearLoad->getEnd())); $yearLoad->setEnd($jdate->jdate('Y/m/d', $yearLoad->getEnd()));
$yearLoad->setNow($jdate->jdate('Y/m/d', time())); $yearLoad->setNow($jdate->jdate('Y/m/d', time()));
return $this->json($yearLoad);
return $this->json($yearLoad, 200, [], ['groups' => ['year:read']]);
} }
#[Route('/api/year/lastyear/info', name: 'app_year_last_year_info')] #[Route('/api/year/lastyear/info', name: 'app_year_last_year_info')]

View file

@ -3,6 +3,8 @@
namespace App\Entity; namespace App\Entity;
use App\Repository\PreInvoiceDocRepository; use App\Repository\PreInvoiceDocRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PreInvoiceDocRepository::class)] #[ORM\Entity(repositoryClass: PreInvoiceDocRepository::class)]
@ -45,6 +47,24 @@ class PreInvoiceDoc
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $amount = null; private ?string $amount = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $taxPercent = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $totalDiscount = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $totalDiscountPercent = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $shippingCost = null;
#[ORM\Column(type: 'boolean', nullable: true)]
private ?bool $showPercentDiscount = null;
#[ORM\Column(type: 'boolean', nullable: true)]
private ?bool $showTotalPercentDiscount = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $mdate = null; private ?string $mdate = null;
@ -66,6 +86,17 @@ class PreInvoiceDoc
#[ORM\ManyToOne(inversedBy: 'preinvoiceDocsSalemans')] #[ORM\ManyToOne(inversedBy: 'preinvoiceDocsSalemans')]
private ?Person $salesman = null; private ?Person $salesman = null;
/**
* @var Collection<int, PreInvoiceItem>
*/
#[ORM\OneToMany(mappedBy: 'doc', targetEntity: PreInvoiceItem::class, orphanRemoval: true)]
private Collection $preInvoiceItems;
public function __construct()
{
$this->preInvoiceItems = new ArrayCollection();
}
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
@ -179,6 +210,72 @@ class PreInvoiceDoc
return $this; return $this;
} }
public function getTaxPercent(): ?string
{
return $this->taxPercent;
}
public function setTaxPercent(?string $taxPercent): static
{
$this->taxPercent = $taxPercent;
return $this;
}
public function getTotalDiscount(): ?string
{
return $this->totalDiscount;
}
public function setTotalDiscount(?string $totalDiscount): static
{
$this->totalDiscount = $totalDiscount;
return $this;
}
public function getTotalDiscountPercent(): ?string
{
return $this->totalDiscountPercent;
}
public function setTotalDiscountPercent(?string $totalDiscountPercent): static
{
$this->totalDiscountPercent = $totalDiscountPercent;
return $this;
}
public function getShippingCost(): ?string
{
return $this->shippingCost;
}
public function setShippingCost(?string $shippingCost): static
{
$this->shippingCost = $shippingCost;
return $this;
}
public function isShowPercentDiscount(): ?bool
{
return $this->showPercentDiscount;
}
public function setShowPercentDiscount(?bool $showPercentDiscount): static
{
$this->showPercentDiscount = $showPercentDiscount;
return $this;
}
public function isShowTotalPercentDiscount(): ?bool
{
return $this->showTotalPercentDiscount;
}
public function setShowTotalPercentDiscount(?bool $showTotalPercentDiscount): static
{
$this->showTotalPercentDiscount = $showTotalPercentDiscount;
return $this;
}
public function getMdate(): ?string public function getMdate(): ?string
{ {
return $this->mdate; return $this->mdate;
@ -262,4 +359,34 @@ class PreInvoiceDoc
return $this; return $this;
} }
/**
* @return Collection<int, PreInvoiceItem>
*/
public function getPreInvoiceItems(): Collection
{
return $this->preInvoiceItems;
}
public function addPreInvoiceItem(PreInvoiceItem $preInvoiceItem): static
{
if (!$this->preInvoiceItems->contains($preInvoiceItem)) {
$this->preInvoiceItems->add($preInvoiceItem);
$preInvoiceItem->setDoc($this);
}
return $this;
}
public function removePreInvoiceItem(PreInvoiceItem $preInvoiceItem): static
{
if ($this->preInvoiceItems->removeElement($preInvoiceItem)) {
// set the owning side to null (unless already changed)
if ($preInvoiceItem->getDoc() === $this) {
$preInvoiceItem->setDoc(null);
}
}
return $this;
}
} }

View file

@ -26,38 +26,27 @@ class PreInvoiceItem
#[ORM\Column(length: 100, nullable: true)] #[ORM\Column(length: 100, nullable: true)]
private ?string $bd = null; private ?string $bd = null;
#[ORM\Column(length: 100, nullable: true)]
private ?string $discountPercent = null;
#[ORM\Column(length: 100, nullable: true)]
private ?string $discountAmount = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $des = null; private ?string $des = null;
#[ORM\ManyToOne]
private ?Person $person = null;
#[ORM\ManyToOne]
private ?BankAccount $bank = null;
#[ORM\ManyToOne]
private ?Cashdesk $cashdesk = null;
#[ORM\ManyToOne]
private ?Salary $salary = null;
#[ORM\ManyToOne(inversedBy: 'preInvoiceItems')]
#[ORM\JoinColumn(nullable: false)]
private ?Business $bid = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Year $year = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $discount = null; private ?string $discount = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
private ?string $tax = null; private ?string $tax = null;
#[ORM\ManyToOne] #[ORM\Column(type: 'boolean', nullable: true)]
private ?bool $showPercentDiscount = null;
#[ORM\ManyToOne(inversedBy: 'preInvoiceItems')]
#[ORM\JoinColumn(nullable: false)] #[ORM\JoinColumn(nullable: false)]
private ?HesabdariTable $refID = null; private ?PreInvoiceDoc $doc = null;
public function getId(): ?int public function getId(): ?int
{ {
@ -112,6 +101,28 @@ class PreInvoiceItem
return $this; return $this;
} }
public function getDiscountPercent(): ?string
{
return $this->discountPercent;
}
public function setDiscountPercent(?string $discountPercent): static
{
$this->discountPercent = $discountPercent;
return $this;
}
public function getDiscountAmount(): ?string
{
return $this->discountAmount;
}
public function setDiscountAmount(?string $discountAmount): static
{
$this->discountAmount = $discountAmount;
return $this;
}
public function getDes(): ?string public function getDes(): ?string
{ {
return $this->des; return $this->des;
@ -124,78 +135,6 @@ class PreInvoiceItem
return $this; return $this;
} }
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): static
{
$this->person = $person;
return $this;
}
public function getBank(): ?BankAccount
{
return $this->bank;
}
public function setBank(?BankAccount $bank): static
{
$this->bank = $bank;
return $this;
}
public function getCashdesk(): ?Cashdesk
{
return $this->cashdesk;
}
public function setCashdesk(?Cashdesk $cashdesk): static
{
$this->cashdesk = $cashdesk;
return $this;
}
public function getSalary(): ?Salary
{
return $this->salary;
}
public function setSalary(?Salary $salary): static
{
$this->salary = $salary;
return $this;
}
public function getBid(): ?Business
{
return $this->bid;
}
public function setBid(?Business $bid): static
{
$this->bid = $bid;
return $this;
}
public function getYear(): ?Year
{
return $this->year;
}
public function setYear(?Year $year): static
{
$this->year = $year;
return $this;
}
public function getDiscount(): ?string public function getDiscount(): ?string
{ {
return $this->discount; return $this->discount;
@ -220,14 +159,25 @@ class PreInvoiceItem
return $this; return $this;
} }
public function getRefID(): ?HesabdariTable public function isShowPercentDiscount(): ?bool
{ {
return $this->refID; return $this->showPercentDiscount;
} }
public function setRefID(?HesabdariTable $refID): static public function setShowPercentDiscount(?bool $showPercentDiscount): static
{ {
$this->refID = $refID; $this->showPercentDiscount = $showPercentDiscount;
return $this;
}
public function getDoc(): ?PreInvoiceDoc
{
return $this->doc;
}
public function setDoc(?PreInvoiceDoc $doc): static
{
$this->doc = $doc;
return $this; return $this;
} }

View file

@ -6,6 +6,7 @@ use App\Repository\YearRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\Ignore; use Symfony\Component\Serializer\Annotation\Ignore;
#[ORM\Entity(repositoryClass: YearRepository::class)] #[ORM\Entity(repositoryClass: YearRepository::class)]
@ -14,9 +15,11 @@ class Year
#[ORM\Id] #[ORM\Id]
#[ORM\GeneratedValue] #[ORM\GeneratedValue]
#[ORM\Column] #[ORM\Column]
#[Groups(['year:read'])]
private ?int $id = null; private ?int $id = null;
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
#[Groups(['year:read'])]
private ?string $label = null; private ?string $label = null;
#[ORM\ManyToOne(inversedBy: 'years')] #[ORM\ManyToOne(inversedBy: 'years')]
@ -25,6 +28,7 @@ class Year
private ?Business $bid = null; private ?Business $bid = null;
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
#[Groups(['year:read'])]
private ?bool $head = null; private ?bool $head = null;
#[ORM\OneToMany(mappedBy: 'year', targetEntity: HesabdariDoc::class, orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'year', targetEntity: HesabdariDoc::class, orphanRemoval: true)]
@ -32,12 +36,15 @@ class Year
private Collection $hesabdariDocs; private Collection $hesabdariDocs;
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
#[Groups(['year:read'])]
private ?string $start = null; private ?string $start = null;
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
#[Groups(['year:read'])]
private ?string $end = null; private ?string $end = null;
#[ORM\Column(length: 255, nullable: true)] #[ORM\Column(length: 255, nullable: true)]
#[Groups(['year:read'])]
private ?string $now = null; private ?string $now = null;
#[ORM\OneToMany(mappedBy: 'year', targetEntity: HesabdariRow::class, orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'year', targetEntity: HesabdariRow::class, orphanRemoval: true)]

View file

@ -0,0 +1,25 @@
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class NumberFormatExtension extends AbstractExtension
{
public function getFilters()
{
return [
new TwigFilter('number_format', [$this, 'formatNumber']),
];
}
public function formatNumber($number, $decimals = 0, $decPoint = '.', $thousandsSep = ',', $currency = null)
{
$formatted = number_format($number, $decimals, $decPoint, $thousandsSep);
if ($currency) {
$formatted .= ' ' . $currency;
}
return $formatted;
}
}

View file

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>پیش فاکتور {{ doc.code }}</title>
<style>
body {
font-family: 'B Nazanin', Arial, sans-serif;
direction: rtl;
margin: 0;
padding: 10px;
font-size: 12px;
}
.header {
text-align: center;
margin-bottom: 10px;
}
.items {
margin-bottom: 10px;
}
.items table {
width: 100%;
border-collapse: collapse;
}
.items th, .items td {
padding: 3px;
border: 1px solid #000;
text-align: center;
}
.summary {
margin-bottom: 10px;
}
.summary table {
width: 100%;
border-collapse: collapse;
}
.summary td {
padding: 3px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div class="header">
<h2>پیش فاکتور فروش</h2>
<p>شماره: {{ doc.code }}</p>
<p>تاریخ: {{ doc.date }}</p>
</div>
<div class="items">
<table>
<thead>
<tr>
<th>نام کالا</th>
<th>تعداد</th>
<th>قیمت</th>
<th>جمع</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.commodity.name }}</td>
<td>{{ item.commodityCount }}</td>
<td>{{ item.bs|number_format }}</td>
<td>{{ (item.bs * item.commodityCount)|number_format }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="summary">
<table>
<tr>
<td>جمع کل:</td>
<td>{{ doc.amount|number_format }}</td>
</tr>
<tr>
<td>تخفیف:</td>
<td>{{ doc.totalDiscount|number_format }}</td>
</tr>
<tr>
<td>مالیات:</td>
<td>{{ (doc.amount * doc.taxPercent / 100)|number_format }}</td>
</tr>
<tr>
<td>هزینه حمل:</td>
<td>{{ doc.shippingCost|number_format }}</td>
</tr>
<tr>
<td>قابل پرداخت:</td>
<td>{{ (doc.amount - doc.totalDiscount + doc.shippingCost + (doc.amount * doc.taxPercent / 100))|number_format }}</td>
</tr>
</table>
</div>
</body>
</html>

View file

@ -0,0 +1,278 @@
<!DOCTYPE html>
<html lang="fa" direction="rtl">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<head>
<style>
.center {
text-align: center;
}
.text-white {
color: white;
}
.stimol td,
.stimol th {
border: 1px solid black;
}
.item {
height: 30px;
}
</style>
</head>
<body style="direction:rtl; width:100%">
<div class="block-content pt-1 pb-3 d-none d-sm-block">
<div class="c-print container-xl">
<div class="tg-wrap" style="width:100%; border:1px solid black;border-radius: 8px;">
<table class="rounded" style="width:100%;">
<thead>
<tr>
<td style="width:20%">
<img src="{{ url('front_avatar_file_get', {id: bid.id}) }}" width="65"/>
</td>
<td style="width:60%; text-align:center">
<h3 class="">پیش فاکتور فروش کالا و خدمات</h3>
</td>
<td style="width:20%">
<h4>
<b>تاریخ:</b>
{{ doc.date }}</h4>
<br/>
<h4>
<b>شماره:</b>
{{ doc.code }}</h4>
</td>
</tr>
</thead>
</table>
</div>
{% if printOptions.bidInfo %}
<div style="width:100%; border:1px solid black;border-radius: 8px;margin-top:5px;text-align:center;">
<div class="tg-wrap" style="width:100%;border-radius: 8px 8px 0px 0px;text-align:center;background-color:gray">
<b style="color:white;">فروشنده</b>
</div>
<table style="width:100%;">
<tbody>
<tr style="text-align:center;">
<td class="">
<p>
<b>نام: </b>
{{ bid.legalName }}
</p>
</td>
<td class="center">
<p>
<b>شناسه ملی: </b>
{{ bid.shenasemeli }}
</p>
</td>
<td class="center">
<p>
<b>شماره ثبت: </b>
{{ bid.shomaresabt }}
</p>
</td>
<td class="center">
<p>
<b>شماره اقتصادی: </b>
{{ bid.codeeghtesadi }}
</p>
</td>
<td class="center">
<p>
<b>تلفن / نمابر:</b>
{{ bid.tel }}
</p>
</td>
</tr>
<tr>
<td class="" colspan="1">
<p>
<b>کد پستی:</b>
{{ bid.postalcode }}
</p>
</td>
<td class="" colspan="3">
<p>
<b>آدرس: </b>
استان {{ bid.ostan }}، شهر {{ bid.shahrestan }}، {{ bid.address }}
</p>
</td>
</tr>
</tbody>
</table>
</div>
{% endif %}
<div style="width:100%; border:1px solid black;border-radius: 8px;margin-top:5px;text-align:center;">
<div class="tg-wrap" style="width:100%;border-radius: 8px 8px 0px 0px;text-align:center;background-color:gray">
<b style="color:white;">خریدار</b>
</div>
<table style="width:100%;">
<tbody>
<tr style="text-align:center;">
<td class="">
<p>
<b>نام: </b>
{% if person.prelabel is not null %}{{ person.prelabel.label }}{% endif %}
{{ person.nikename }}
</p>
</td>
<td class="center">
<p>
<b>شناسه ملی: </b>
{{ person.shenasemeli }}
</p>
</td>
<td class="center">
<p>
<b>شماره ثبت: </b>
{{ person.sabt }}
</p>
</td>
<td class="center">
<p>
<b>شماره اقتصادی: </b>
{{ person.codeeghtesadi }}
</p>
</td>
<td class="center">
<p>
<b>تلفن / نمابر:</b>
{{ person.tel }}
</p>
</td>
</tr>
<tr>
<td class="" colspan="1">
<p>
<b>کد پستی:</b>
{{ person.postalcode }}
</p>
</td>
<td class="" colspan="3">
<p>
<b>آدرس: </b>
استان {{ person.ostan }}، شهر {{ person.shahr }}، {{ person.address }}
</p>
</td>
</tr>
</tbody>
</table>
</div>
<div style="width:100%;margin-top:5px;text-align:center;">
<table style="width:100%;">
<thead>
<tr class="stimol" style="background-color:gray;">
<th class="text-white" style="width:80px">ردیف</th>
<th class="text-white">کالا/خدمات</th>
<th class="text-white">شرح</th>
<th class="text-white">تعداد / مقدار</th>
<th class="text-white">مبلغ واحد</th>
{% if printOptions.discountInfo %}
<th class="text-white">تخفیف</th>
{% endif %}
{% if printOptions.taxInfo %}
<th class="text-white">مالیات</th>
{% endif %}
<th class="text-white">مبلغ کل</th>
</tr>
</thead>
<tbody>
{% set taxAll = 0 %}
{% set rowIndex = 0 %}
{% for item in items %}
{% if doc.taxPercent %}
{% set itemTax = (item.bs * item.commodityCount * doc.taxPercent / 100) | round %}
{% else %}
{% set itemTax = item.tax %}
{% endif %}
{% set taxAll = taxAll + itemTax %}
{% set rowIndex = rowIndex + 1 %}
<tr class="stimol">
<td class="center item">{{ rowIndex }}</td>
<td class="center item">
{{ item.commodity.code }} - {{ item.commodity.name }}
</td>
<td class="center item">{{ item.des }}</td>
<td class="center item">
{{ item.commodityCount }} {{ item.commodity.unit.name }}
</td>
<td class="center item">{{ item.bs|number_format(0, '.', ',', doc.money.shortName) }}</td>
{% if printOptions.discountInfo %}
<td class="center item">
{% if item.showPercentDiscount %}
{{ item.discountPercent }}%
({{ (item.bs * item.commodityCount * item.discountPercent / 100)|round|number_format(0, '.', ',', doc.money.shortName) }})
{% else %}
{{ item.discountAmount|number_format(0, '.', ',', doc.money.shortName) }}
{% endif %}
</td>
{% endif %}
{% if printOptions.taxInfo %}
<td class="center item">
{{ itemTax|number_format(0, '.', ',', doc.money.shortName) }}
</td>
{% endif %}
<td class="center item">
{% if item.showPercentDiscount %}
{{ (item.bs * item.commodityCount - (item.bs * item.commodityCount * item.discountPercent / 100)|round)|number_format(0, '.', ',', doc.money.shortName) }}
{% else %}
{{ (item.bs * item.commodityCount - item.discountAmount)|number_format(0, '.', ',', doc.money.shortName) }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div style="width:100%;margin-top:0px;text-align:center;">
<table style="width:100%;">
<tbody>
<tr class="stimol">
<td class="item" style="width:70%;padding:1%">
<h4>
توضیحات: {{ doc.des }}
<br>
{% if printOptions.note == true %}
<h4 class="">یادداشت:</h4>
<ul class="">
<li class="">{{ note }}</li>
</ul>
{% endif %}
</h4>
</td>
<td class="item" style="width:15%;padding:1%">
{% set totalDiscount = doc.showTotalPercentDiscount ? (doc.amount * doc.totalDiscountPercent / 100) | round : doc.totalDiscount %}
<h4>تخفیف: {{ totalDiscount|number_format(0, '.', ',', doc.money.shortName) }}</h4>
<h4>مالیات: {{ taxAll|number_format(0, '.', ',', doc.money.shortName) }}</h4>
<h4>حمل و نقل: {{ doc.shippingCost|number_format(0, '.', ',', doc.money.shortName) }}</h4>
<h4>جمع کل: {{ doc.amount|number_format(0, '.', ',', doc.money.shortName) }}</h4>
<h4>جمع کل نهایی: {{ (doc.amount + taxAll - totalDiscount + doc.shippingCost)|number_format(0, '.', ',', doc.money.shortName) }}</h4>
</td>
</tr>
</tbody>
</table>
</div>
<div style="width:40%;margin-top:0px;text-align:center;float:left;">
<table style="width:100%;">
<tbody>
<tr class="">
<td class="center" style="height:90px">
<h4>مهر و امضا خریدار</h4>
</td>
<td class="center" style="height:90px">
<h4>مهر و امضا فروشنده:</h4>
<br>
<img src="{{ url('front_seal_file_get', {id: bid.id}) }}" width="160"/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,527 @@
<template>
<div>
<v-menu v-model="menu" :close-on-content-click="false">
<template v-slot:activator="{ props }">
<v-text-field
v-bind="props"
v-model="displayValue"
variant="outlined"
:error-messages="errorMessages"
:rules="combinedRules"
:label="label"
class=""
prepend-inner-icon="mdi-package-variant"
clearable
@click:clear="clearSelection"
:loading="loading"
@keydown.enter="handleEnter"
hide-details="auto"
>
<template v-slot:append-inner>
<v-icon>{{ menu ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
</template>
</v-text-field>
</template>
<v-card min-width="300" max-width="400">
<v-card-text class="pa-2">
<template v-if="!loading">
<v-list density="compact" class="list-container">
<template v-if="filteredItems.length > 0">
<v-list-item
v-for="item in filteredItems"
:key="item.id"
@click="selectItem(item)"
class="mb-1"
>
<v-list-item-title class="text-right">{{ item.name }}</v-list-item-title>
<v-list-item-subtitle class="text-right">{{ item.code }}</v-list-item-subtitle>
</v-list-item>
</template>
<template v-else>
<v-list-item>
<v-list-item-title class="text-center text-grey">
نتیجهای یافت نشد
</v-list-item-title>
</v-list-item>
</template>
</v-list>
<v-btn
v-if="filteredItems.length === 0"
block
color="primary"
class="mt-2"
@click="showAddDialog = true"
>
افزودن کالا/خدمت جدید
</v-btn>
</template>
<v-progress-circular
v-else
indeterminate
color="primary"
class="d-flex mx-auto my-4"
></v-progress-circular>
</v-card-text>
</v-card>
</v-menu>
<v-dialog v-model="showAddDialog" :fullscreen="$vuetify.display.mobile" max-width="800">
<v-card>
<v-toolbar color="primary" density="compact" class="sticky-toolbar">
<v-toolbar-title>افزودن کالا/خدمت جدید</v-toolbar-title>
<v-spacer></v-spacer>
<v-tooltip text="بستن">
<template v-slot:activator="{ props }">
<v-btn
icon="mdi-close"
v-bind="props"
@click="showAddDialog = false"
></v-btn>
</template>
</v-tooltip>
<v-tooltip text="ذخیره">
<template v-slot:activator="{ props }">
<v-btn
icon="mdi-content-save"
v-bind="props"
@click="saveCommodity"
:loading="saving"
></v-btn>
</template>
</v-tooltip>
</v-toolbar>
<v-tabs
v-model="tabs"
color="primary"
show-arrows
class="sticky-tabs"
>
<v-tab value="basic" class="flex-grow-1">اطلاعات پایه</v-tab>
<v-tab value="details" class="flex-grow-1">جزئیات</v-tab>
<v-tab value="pricing" class="flex-grow-1">قیمتگذاری</v-tab>
</v-tabs>
<v-card-text class="content-container">
<v-window v-model="tabs">
<v-window-item value="basic">
<v-form @submit.prevent="saveCommodity">
<v-row class="mt-4">
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.name"
label="نام کالا/خدمت *"
required
:error-messages="nameErrors"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.code"
label="کد"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="newCommodity.type"
:items="commodityTypes"
label="نوع *"
required
:error-messages="typeErrors"
></v-select>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="newCommodity.unit"
:items="units"
label="واحد اندازه‌گیری *"
required
:error-messages="unitErrors"
></v-select>
</v-col>
<v-col cols="12">
<v-textarea
v-model="newCommodity.description"
label="توضیحات"
rows="3"
></v-textarea>
</v-col>
</v-row>
</v-form>
</v-window-item>
<v-window-item value="details">
<v-row class="mt-4">
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.brand"
label="برند"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.model"
label="مدل"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.barcode"
label="بارکد"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.serial"
label="سریال"
></v-text-field>
</v-col>
<v-col cols="12">
<v-switch
v-model="newCommodity.isService"
label="خدمت"
color="primary"
></v-switch>
</v-col>
</v-row>
</v-window-item>
<v-window-item value="pricing">
<v-row class="mt-4">
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.basePrice"
label="قیمت پایه"
type="number"
prefix="ریال"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.salePrice"
label="قیمت فروش"
type="number"
prefix="ریال"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.minStock"
label="حداقل موجودی"
type="number"
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="newCommodity.maxStock"
label="حداکثر موجودی"
type="number"
></v-text-field>
</v-col>
</v-row>
</v-window-item>
</v-window>
</v-card-text>
</v-card>
</v-dialog>
<v-snackbar
v-model="snackbar.show"
:color="snackbar.color"
:timeout="3000"
>
{{ snackbar.text }}
<template v-slot:actions>
<v-btn
color="white"
variant="text"
@click="snackbar.show = false"
>
بستن
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import axios from 'axios'
export default defineComponent({
name: 'Hcommoditysearch',
props: {
modelValue: {
type: [Object, Number],
default: null
},
label: {
type: String,
default: 'کالا/خدمت'
},
returnObject: {
type: Boolean,
default: false
},
rules: {
type: Array,
default: () => []
}
},
data() {
return {
selectedItem: null,
items: [],
loading: false,
menu: false,
searchQuery: '',
totalItems: 0,
currentPage: 1,
itemsPerPage: 10,
searchTimeout: null,
showAddDialog: false,
tabs: 'basic',
saving: false,
commodityTypes: ['کالا', 'خدمت'],
units: ['عدد', 'کیلوگرم', 'گرم', 'متر', 'سانتیمتر', 'لیتر', 'متر مربع', 'متر مکعب'],
snackbar: {
show: false,
text: '',
color: 'success'
},
errorMessages: [],
newCommodity: {
name: '',
code: '',
type: '',
unit: '',
description: '',
brand: '',
model: '',
barcode: '',
serial: '',
isService: false,
basePrice: 0,
salePrice: 0,
minStock: 0,
maxStock: 0
}
}
},
computed: {
filteredItems() {
return Array.isArray(this.items) ? this.items : []
},
displayValue: {
get() {
if (this.menu) {
return this.searchQuery
}
return this.selectedItem ? this.selectedItem.name : this.searchQuery
},
set(value) {
this.searchQuery = value
if (!value) {
this.clearSelection()
}
}
},
nameErrors() {
if (!this.newCommodity.name) return ['نام کالا/خدمت الزامی است']
return []
},
typeErrors() {
if (!this.newCommodity.type) return ['نوع کالا/خدمت الزامی است']
return []
},
unitErrors() {
if (!this.newCommodity.unit) return ['واحد اندازه‌گیری الزامی است']
return []
},
combinedRules() {
return [
v => !!v || 'انتخاب کالا/خدمت الزامی است',
...this.rules
]
}
},
watch: {
modelValue: {
handler(newVal) {
if (this.returnObject) {
this.selectedItem = newVal
} else {
this.selectedItem = this.items.find(item => item.id === newVal)
}
},
immediate: true
},
searchQuery: {
handler(newVal) {
this.currentPage = 1
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
this.searchTimeout = setTimeout(() => {
this.fetchData()
}, 500)
}
},
showAddDialog: {
handler(newVal) {
if (newVal) {
this.newCommodity.name = this.searchQuery
}
}
}
},
methods: {
showMessage(text: string, color = 'success') {
this.snackbar.text = text
this.snackbar.color = color
this.snackbar.show = true
},
async fetchData() {
this.loading = true
try {
const response = await axios.post('/api/commodity/list', {
page: this.currentPage,
itemsPerPage: this.itemsPerPage,
search: this.searchQuery,
sortBy: null
})
if (response.data && Array.isArray(response.data)) {
this.items = response.data
this.totalItems = response.data.length
} else if (response.data && response.data.items) {
this.items = response.data.items
this.totalItems = response.data.total || response.data.items.length
} else {
this.items = []
this.totalItems = 0
}
if (this.modelValue) {
if (this.returnObject) {
this.selectedItem = this.modelValue
} else {
this.selectedItem = this.items.find(item => item.id === this.modelValue)
}
}
} catch (error) {
this.showMessage('خطا در بارگذاری داده‌ها', 'error')
this.items = []
this.totalItems = 0
} finally {
this.loading = false
}
},
async saveCommodity() {
if (!this.newCommodity.name) {
this.showMessage('نام کالا/خدمت الزامی است', 'error')
return
}
if (!this.newCommodity.type) {
this.showMessage('نوع کالا/خدمت الزامی است', 'error')
return
}
if (!this.newCommodity.unit) {
this.showMessage('واحد اندازه‌گیری الزامی است', 'error')
return
}
this.saving = true
try {
const response = await axios.post('/api/commodity/mod/' + this.newCommodity.code, this.newCommodity)
if (response.data.Success) {
if (response.data.result === 1) {
this.showMessage('کالا/خدمت با موفقیت ثبت شد')
this.showAddDialog = false
this.fetchData()
} else if (response.data.result === 2) {
this.showMessage('این کالا/خدمت قبلاً ثبت شده است', 'error')
}
} else {
this.showMessage('خطا در ثبت کالا/خدمت', 'error')
}
} catch (error) {
console.error('خطا در ثبت کالا/خدمت:', error)
this.showMessage('خطا در ثبت کالا/خدمت', 'error')
} finally {
this.saving = false
}
},
selectItem(item: any) {
this.selectedItem = item
this.searchQuery = item.name
this.$emit('update:modelValue', this.returnObject ? item : item.id)
this.menu = false
this.errorMessages = []
},
clearSelection() {
this.selectedItem = null
this.searchQuery = ''
this.$emit('update:modelValue', null)
this.errorMessages = ['انتخاب کالا/خدمت الزامی است']
},
handleEnter() {
if (!this.loading && this.filteredItems.length === 0) {
this.showAddDialog = true
}
}
},
created() {
this.fetchData()
}
})
</script>
<style scoped>
.list-container {
max-height: 300px;
overflow-y: auto;
}
.content-container {
max-height: 500px;
overflow-y: auto;
}
.sticky-toolbar {
position: sticky;
top: 0;
z-index: 1;
}
.sticky-tabs {
position: sticky;
top: 48px;
z-index: 1;
overflow-x: auto;
white-space: nowrap;
}
:deep(.v-menu__content) {
position: fixed !important;
z-index: 9999 !important;
transform-origin: center top !important;
}
:deep(.v-overlay__content) {
position: fixed !important;
}
@media (max-width: 600px) {
.content-container {
max-height: calc(100vh - 120px);
}
.sticky-tabs {
-webkit-overflow-scrolling: touch;
}
}
</style>

View file

@ -9,7 +9,11 @@
dir="ltr" dir="ltr"
dense dense
hide-details="auto" hide-details="auto"
/> >
<template v-for="(_, slot) in $slots" #[slot]="props">
<slot :name="slot" v-bind="props" />
</template>
</v-text-field>
</template> </template>
<script> <script>
@ -89,4 +93,9 @@ export default {
direction: ltr; direction: ltr;
text-align: left; text-align: left;
} }
:deep(.v-text-field .v-input__prepend-inner) {
padding-right: 0;
margin-right: 0;
}
</style> </style>

View file

@ -11,7 +11,10 @@ const en_lang = {
ok: "Ok", ok: "Ok",
cancel: "Cancel", cancel: "Cancel",
save: "Save", save: "Save",
active_account:"Active Account" active_account:"Active Account",
presell_info: "Presell Information",
financial_info: "Financial Information",
invoice_items: "Invoice Items",
}, },
app:{ app:{
name:"Hesabix", name:"Hesabix",
@ -75,6 +78,14 @@ const en_lang = {
businesses: "Businesses", businesses: "Businesses",
business_create: "Create Business", business_create: "Create Business",
} }
} },
drawer:{
cheques: "Cheques",
cheque: "Cheque",
cheque_input: "Cheque Input",
cheque_output: "Cheque Output",
presells: "Presells",
presell_view: "View Presell",
},
}; };
export default en_lang export default en_lang

View file

@ -113,6 +113,8 @@ const fa_lang = {
transfer: "انتقال", transfer: "انتقال",
cheques: "چک‌ها", cheques: "چک‌ها",
cheque: "چک", cheque: "چک",
cheque_input: "دریافت چک",
cheque_output: "صدور چک",
commodity: "کالا و خدمات", commodity: "کالا و خدمات",
commodity_list: "فهرست کالا و خدمات", commodity_list: "فهرست کالا و خدمات",
docs: "اسناد حسابداری", docs: "اسناد حسابداری",
@ -160,6 +162,7 @@ const fa_lang = {
accounting: "حسابداری", accounting: "حسابداری",
accounting_table: "جدول حساب‌ها", accounting_table: "جدول حساب‌ها",
presells: "پیش‌ فاکتور‌ها", presells: "پیش‌ فاکتور‌ها",
presell_view: "مشاهده پیش فاکتور",
reports: "گزارشات", reports: "گزارشات",
settings: "تنظیمات", settings: "تنظیمات",
bid_settings: "تنظیمات کسب‌و‌کار", bid_settings: "تنظیمات کسب‌و‌کار",
@ -465,6 +468,9 @@ const fa_lang = {
"manage_columns": "مدیریت ستون‌ها", "manage_columns": "مدیریت ستون‌ها",
customize_columns: "شخصی‌سازی ستون‌ها", customize_columns: "شخصی‌سازی ستون‌ها",
close_dialog: "بستن", close_dialog: "بستن",
presell_info: "اطلاعات پیش فاکتور",
financial_info: "اطلاعات مالی",
invoice_items: "اقلام فاکتور",
}, },
app: { app: {
loading: "در حال بارگذاری...", loading: "در حال بارگذاری...",

View file

@ -152,7 +152,7 @@ export default {
{ path: '/acc/costs/list', key: 'G', label: this.$t('drawer.costs'), ctrl: true, shift: true, permission: () => this.permissions.cost }, { path: '/acc/costs/list', key: 'G', label: this.$t('drawer.costs'), ctrl: true, shift: true, permission: () => this.permissions.cost },
{ path: '/acc/sell/fast-mod', key: 'J', label: this.$t('drawer.fast_sell'), ctrl: true, shift: true, permission: () => this.permissions.sell }, { path: '/acc/sell/fast-mod', key: 'J', label: this.$t('drawer.fast_sell'), ctrl: true, shift: true, permission: () => this.permissions.sell },
{ path: '/acc/sell/list', key: 'V', label: this.$t('drawer.sell_invoices'), ctrl: true, shift: true, permission: () => this.permissions.sell }, { path: '/acc/sell/list', key: 'V', label: this.$t('drawer.sell_invoices'), ctrl: true, shift: true, permission: () => this.permissions.sell },
{ path: '/acc/presell/list', key: 'X', label: this.$t('drawer.presells'), ctrl: true, shift: true, permission: () => this.permissions.sell && this.isPluginActive('accpro') && (1 == 2) }, { path: '/acc/presell/list', key: 'X', label: this.$t('drawer.presells'), ctrl: true, shift: true, permission: () => this.permissions.sell && this.isPluginActive('accpro') },
{ path: '/acc/rfsell/list', key: 'Z', label: this.$t('drawer.rfsell_invoices'), ctrl: true, shift: true, permission: () => this.permissions.plugAccproRfsell && this.isPluginActive('accpro') }, { path: '/acc/rfsell/list', key: 'Z', label: this.$t('drawer.rfsell_invoices'), ctrl: true, shift: true, permission: () => this.permissions.plugAccproRfsell && this.isPluginActive('accpro') },
{ path: '/acc/incomes/list', key: 'A', label: this.$t('drawer.incomes'), ctrl: true, shift: true, permission: () => this.permissions.income }, { path: '/acc/incomes/list', key: 'A', label: this.$t('drawer.incomes'), ctrl: true, shift: true, permission: () => this.permissions.income },
{ path: '/acc/accounting/list', key: '1', label: this.$t('drawer.accounting_docs'), ctrl: true, shift: true, permission: () => this.permissions.accounting }, { path: '/acc/accounting/list', key: '1', label: this.$t('drawer.accounting_docs'), ctrl: true, shift: true, permission: () => this.permissions.accounting },
@ -468,6 +468,27 @@ export default {
{{ $t('drawer.cheques') }} {{ $t('drawer.cheques') }}
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/cheque/list') }}</span> <span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/cheque/list') }}</span>
</v-list-item-title> </v-list-item-title>
<template v-slot:append>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn v-bind="props" icon="mdi-plus-box" variant="plain" @click.stop.prevent />
</template>
<v-list>
<v-list-item to="/acc/cheque/input">
<v-list-item-title>
<v-icon color="success" class="mr-2">mdi-arrow-down-bold</v-icon>
{{ $t('drawer.cheque_input') }}
</v-list-item-title>
</v-list-item>
<v-list-item to="/acc/cheque/output">
<v-list-item-title>
<v-icon color="error" class="mr-2">mdi-arrow-up-bold</v-icon>
{{ $t('drawer.cheque_output') }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-list-item> </v-list-item>
<v-list-item v-if="permissions.bankTransfer" to="/acc/transfer/list"> <v-list-item v-if="permissions.bankTransfer" to="/acc/transfer/list">
<v-list-item-title> <v-list-item-title>
@ -600,7 +621,7 @@ export default {
</v-tooltip> </v-tooltip>
</template> </template>
</v-list-item> </v-list-item>
<v-list-item v-if="this.isPluginActive('accpro') && permissions.sell && (1 == 2)" to="/acc/presell/list"> <v-list-item v-if="this.isPluginActive('accpro') && permissions.sell" to="/acc/presell/list">
<v-list-item-title> <v-list-item-title>
{{ $t('drawer.presells') }} {{ $t('drawer.presells') }}
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/presell/list') }}</span> <span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/presell/list') }}</span>
@ -650,7 +671,7 @@ export default {
{{ $t('drawer.accounting_docs') }} {{ $t('drawer.accounting_docs') }}
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/accounting/list') }}</span> <span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/accounting/list') }}</span>
</v-list-item-title> </v-list-item-title>
<template v-slot:append v-if="isPluginActive('accpro') && 1==2"> <template v-slot:append v-if="isPluginActive('accpro')">
<v-tooltip :text="$t('dialog.add_new')" location="end"> <v-tooltip :text="$t('dialog.add_new')" location="end">
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<v-btn v-bind="props" icon="mdi-plus-box" variant="plain" to="/acc/accounting/mod/" /> <v-btn v-bind="props" icon="mdi-plus-box" variant="plain" to="/acc/accounting/mod/" />

View file

@ -6,24 +6,49 @@ import { ref } from 'vue';
import Loading from 'vue-loading-overlay'; import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/css/index.css'; import 'vue-loading-overlay/dist/css/index.css';
interface MostDesItem {
id: number;
des: string;
}
interface SubmitData {
id: number | null;
des: string;
}
export default defineComponent({ export default defineComponent({
name: "mostdes", name: "mostdes",
components: { components: {
Loading Loading
}, },
props: { props: {
submitData: Object, submitData: {
type: String, type: Object as () => SubmitData,
required: true
},
type: {
type: String,
required: true
},
modelValue: {
type: String,
default: ''
},
label: {
type: String,
default: ''
}
}, },
emits: ['update:modelValue'],
watch: { watch: {
search: { search: {
handler: function (val, oldVal) { handler: function (val: string, oldVal: string) {
if (val == '') { if (val == '') {
this.items = this.orgItems; this.items = this.orgItems;
} }
else { else {
const temp = []; const temp: MostDesItem[] = [];
this.orgItems.forEach((item) => { this.orgItems.forEach((item: MostDesItem) => {
if (item.des.includes(val)) { if (item.des.includes(val)) {
temp.push(item); temp.push(item);
} }
@ -37,10 +62,10 @@ export default defineComponent({
data: () => { data: () => {
return { return {
loading: ref(false), loading: ref(false),
items: [], items: [] as MostDesItem[],
orgItems: [], orgItems: [] as MostDesItem[],
des: '', des: '',
selected: 0, selected: null as MostDesItem | null,
search: '', search: '',
dialog: false, dialog: false,
} }
@ -57,7 +82,7 @@ export default defineComponent({
this.loading = false; this.loading = false;
}); });
}, },
remove(id: any) { remove(id: number) {
this.loading = true; this.loading = true;
axios.post('/api/mostdes/remove/' + id).then((response) => { axios.post('/api/mostdes/remove/' + id).then((response) => {
this.loading = false; this.loading = false;
@ -95,12 +120,14 @@ export default defineComponent({
}); });
}) })
} }
}, },
selectItem(item: any) { selectItem(item: MostDesItem) {
this.selected = item; this.selected = item;
this.$props.submitData.id = item.id; if (this.$props.submitData) {
this.$props.submitData.des = item.des; this.$props.submitData.id = item.id;
this.$props.submitData.des = item.des;
this.$emit('update:modelValue', item.des);
}
this.dialog = false; this.dialog = false;
} }
}, },
@ -118,7 +145,7 @@ export default defineComponent({
</v-tooltip> </v-tooltip>
<v-dialog v-model="dialog"> <v-dialog v-model="dialog">
<v-card> <v-card>
<v-card-header> <v-card-title>
<v-toolbar class="fixed-top" color="toolbar" :title="$t('dialog.most_des')"> <v-toolbar class="fixed-top" color="toolbar" :title="$t('dialog.most_des')">
<template v-slot:append> <template v-slot:append>
<v-tooltip :text="$t('dialog.close')" location="bottom"> <v-tooltip :text="$t('dialog.close')" location="bottom">
@ -129,7 +156,7 @@ export default defineComponent({
</v-tooltip> </v-tooltip>
</template> </template>
</v-toolbar> </v-toolbar>
</v-card-header> </v-card-title>
<v-card-text class="mt-5"> <v-card-text class="mt-5">
<v-text-field class="mb-2" v-model="search" :loading="loading" prepend-inner-icon="mdi-magnify" <v-text-field class="mb-2" v-model="search" :loading="loading" prepend-inner-icon="mdi-magnify"
:label="$t('dialog.search')" variant="outlined" hide-details single-line></v-text-field> :label="$t('dialog.search')" variant="outlined" hide-details single-line></v-text-field>

View file

@ -19,28 +19,6 @@
<v-btn v-bind="props" icon="mdi-delete" color="danger" @click="deleteItems()"></v-btn> <v-btn v-bind="props" icon="mdi-delete" color="danger" @click="deleteItems()"></v-btn>
</template> </template>
</v-tooltip> </v-tooltip>
<v-menu>
<template v-slot:activator="{ props }">
<v-btn v-bind="props" icon="" color="green">
<v-tooltip activator="parent" :text="$t('dialog.change_labels')" location="bottom" />
<v-icon icon="mdi-dots-horizontal-circle"></v-icon>
</v-btn>
</template>
<v-list>
<v-list-subheader color="primary">{{ $t('dialog.change_labels') }}</v-list-subheader>
<v-list-item v-for="item in types" class="text-dark" :title="$t('dialog.change_to') + ' ' + item.label"
@click="changeLabel(item)">
<template v-slot:prepend>
<v-icon color="green-darken-4" icon="mdi-label"></v-icon>
</template>
</v-list-item>
<v-list-item class="text-dark" :title="$t('dialog.delete_labels')" @click="changeLabel('clear')">
<template v-slot:prepend>
<v-icon color="red" icon="mdi-undo"></v-icon>
</template>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar> </v-toolbar>
<v-row class="pa-1"> <v-row class="pa-1">
<v-col> <v-col>
@ -53,30 +31,6 @@
</template> </template>
</v-tooltip> </v-tooltip>
</template> </template>
<template v-slot:append-inner>
<v-menu :close-on-content-click="false">
<template v-slot:activator="{ props }">
<v-icon size="sm" v-bind="props" icon="" color="primary">
<v-tooltip activator="parent" variant="plain" :text="$t('dialog.filters')" location="bottom" />
<v-icon icon="mdi-filter"></v-icon>
</v-icon>
</template>
<v-list>
<v-list-subheader color="primary">
<v-icon icon="mdi-filter"></v-icon>
{{ $t('dialog.filters') }}</v-list-subheader>
<v-list-item v-for="(item, index) in types" class="text-dark">
<template v-slot:title>
<div class="form-check form-check-inline mx-1">
<input @change="filterTable()" v-model="types[index].checked" checked="" class="form-check-input"
type="checkbox">
<label class="form-check-label">{{ item.label }}</label>
</div>
</template>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-text-field> </v-text-field>
<EasyDataTable table-class-name="customize-table" :table-class-name="tableClassName" <EasyDataTable table-class-name="customize-table" :table-class-name="tableClassName"
v-model:items-selected="itemsSelected" multi-sort show-index alternating :search-value="searchValue" v-model:items-selected="itemsSelected" multi-sort show-index alternating :search-value="searchValue"
@ -100,7 +54,7 @@
<v-icon icon="mdi-file-pdf-box"></v-icon> <v-icon icon="mdi-file-pdf-box"></v-icon>
</template> </template>
</v-list-item> </v-list-item>
<v-list-item class="text-dark" :title="$t('dialog.edit')" @click="canEditItem(code)"> <v-list-item class="text-dark" :title="$t('dialog.edit')" :to="'/acc/presell/mod/' + code">
<template v-slot:prepend> <template v-slot:prepend>
<v-icon icon="mdi-file-edit"></v-icon> <v-icon icon="mdi-file-edit"></v-icon>
</template> </template>
@ -113,15 +67,8 @@
</v-list> </v-list>
</v-menu> </v-menu>
</template> </template>
<template #item-label="{ label }">
<span v-if="label">
<span v-if="label.code == 'payed'" class="text-success">{{ label.label }}</span>
<span v-if="label.code == 'returned'" class="text-danger">{{ label.label }}</span>
<span v-if="label.code == 'accepted'" class="text-info">{{ label.label }}</span>
</span>
</template>
<template #item-des="{ des }"> <template #item-des="{ des }">
{{ des.replace("فاکتور فروش:", "") }} {{ des.replace("پیش فاکتور فروش:", "") }}
</template> </template>
<template #item-relatedDocsCount="{ relatedDocsCount, relatedDocsPays }"> <template #item-relatedDocsCount="{ relatedDocsCount, relatedDocsPays }">
<span v-if="relatedDocsCount != '0'" class="text-success"><i class="fa fa-money"></i> <span v-if="relatedDocsCount != '0'" class="text-success"><i class="fa fa-money"></i>
@ -152,6 +99,11 @@
{{ $filters.formatNumber(discountAll) }} {{ $filters.formatNumber(discountAll) }}
</span> </span>
</template> </template>
<template #item-totalAmount="{ totalAmount }">
<span class="text-dark">
{{ $filters.formatNumber(totalAmount) }}
</span>
</template>
<template #item-person="{ person }"> <template #item-person="{ person }">
<router-link :to="'/acc/persons/card/view/' + person.code"> <router-link :to="'/acc/persons/card/view/' + person.code">
{{ person.nikename }} {{ person.nikename }}
@ -173,8 +125,6 @@
</v-select> </v-select>
<v-switch inset v-model="printOptions.bidInfo" color="primary" :label="$t('dialog.bid_info_label')" <v-switch inset v-model="printOptions.bidInfo" color="primary" :label="$t('dialog.bid_info_label')"
hide-details></v-switch> hide-details></v-switch>
<v-switch inset v-model="printOptions.pays" color="primary" :label="$t('dialog.invoice_pays')"
hide-details></v-switch>
<v-switch inset v-model="printOptions.note" color="primary" :label="$t('dialog.invoice_footer_note')" <v-switch inset v-model="printOptions.note" color="primary" :label="$t('dialog.invoice_footer_note')"
hide-details></v-switch> hide-details></v-switch>
<v-switch inset v-model="printOptions.taxInfo" color="primary" :label="$t('dialog.tax_dexpo')" <v-switch inset v-model="printOptions.taxInfo" color="primary" :label="$t('dialog.tax_dexpo')"
@ -192,12 +142,69 @@
</v-card> </v-card>
</v-dialog> </v-dialog>
<!-- End Print Modal --> <!-- End Print Modal -->
<!-- Delete Dialog -->
<v-dialog v-model="deleteDialog" width="auto">
<v-card>
<v-card-title class="text-h5">
حذف فاکتور
</v-card-title>
<v-card-text>
آیا مطمئن هستید؟
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" variant="text" @click="confirmDelete">
بله
</v-btn>
<v-btn color="error" variant="text" @click="deleteDialog = false">
خیر
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Delete Group Dialog -->
<v-dialog v-model="deleteGroupDialog" width="auto">
<v-card>
<v-card-title class="text-h5">
حذف گروهی
</v-card-title>
<v-card-text>
آیا مطمئن هستید؟
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" variant="text" @click="confirmDeleteGroup">
بله
</v-btn>
<v-btn color="error" variant="text" @click="deleteGroupDialog = false">
خیر
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Snackbar -->
<v-snackbar
v-model="snackbar.show"
:color="snackbar.color"
:timeout="3000"
>
{{ snackbar.text }}
<template v-slot:actions>
<v-btn
color="white"
variant="text"
@click="snackbar.show = false"
>
بستن
</v-btn>
</template>
</v-snackbar>
</template> </template>
<script> <script>
import axios from "axios"; import axios from "axios";
import Swal from "sweetalert2"; import { ref, defineComponent } from "vue";
import { ref ,defineComponent } from "vue";
export default defineComponent ({ export default defineComponent ({
name: "list", name: "list",
@ -224,7 +231,6 @@ export default defineComponent ({
], ],
modal: false, modal: false,
printOptions: { printOptions: {
pays: true,
note: true, note: true,
bidInfo: true, bidInfo: true,
taxInfo: true, taxInfo: true,
@ -236,7 +242,6 @@ export default defineComponent ({
sumTotal: 0, sumTotal: 0,
itemsSelected: [], itemsSelected: [],
searchValue: '', searchValue: '',
types: [],
loading: ref(true), loading: ref(true),
items: [], items: [],
orgItems: [], orgItems: [],
@ -248,99 +253,26 @@ export default defineComponent ({
{ text: "تخفیف", value: "discountAll", sortable: true }, { text: "تخفیف", value: "discountAll", sortable: true },
{ text: "حمل و نقل", value: "transferCost", sortable: true }, { text: "حمل و نقل", value: "transferCost", sortable: true },
{ text: "مبلغ", value: "amount", sortable: true }, { text: "مبلغ", value: "amount", sortable: true },
{ text: "سود فاکتور", value: "profit", sortable: true }, { text: "مبلغ کل", value: "totalAmount", sortable: true },
{ text: "پرداختی", value: "relatedDocsCount", sortable: true },
{ text: "برچسب", value: "label", width: 100 },
{ text: "شرح", value: "des", sortable: true }, { text: "شرح", value: "des", sortable: true },
] ],
deleteDialog: false,
deleteGroupDialog: false,
selectedCode: null,
snackbar: {
show: false,
text: '',
color: 'success'
}
} }
}, },
methods: { methods: {
changeLabel(label) {
if (this.itemsSelected.length == 0) {
Swal.fire({
text: 'هیچ موردی انتخاب نشده است.',
icon: 'warning',
confirmButtonText: 'قبول'
});
}
else {
this.loading = true;
axios.post('/api/presell/label/change', {
'items': this.itemsSelected,
'label': label
}
).then((response) => {
this.loading == false;
if (response.data.code == 0) {
Swal.fire({
text: 'فاکتور‌ها با موفقیت ویرایش شد.',
icon: 'success',
confirmButtonText: 'قبول'
});
this.itemsSelected = [];
}
else if (response.data.result == 2) {
Swal.fire({
text: response.data.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
}
this.loadData();
})
}
},
filterTable() {
this.loading = true;
let calcItems = [];
let isAll = true;
let selectedTypes = [];
this.types.forEach((item) => {
if (item.checked == true) {
isAll = false;
selectedTypes.push(item);
}
});
if (isAll) {
this.items = this.orgItems;
}
else {
this.orgItems.forEach((item) => {
selectedTypes.forEach((st) => {
if (item.label) {
if (st.code == item.label.code) {
let existBefore = false;
calcItems.forEach((ri) => {
if (item.label.code == ri.code) {
existBefore = true;
}
})
if (existBefore == false) {
calcItems.push(item);
}
}
}
});
});
this.items = calcItems;
}
this.loading = false;
},
loadData() { loadData() {
axios.post("/api/printers/options/info").then((response) => { axios.post("/api/printers/options/info").then((response) => {
this.printOptions = response.data.sell; this.printOptions = response.data.sell;
}); });
axios.post('/api/invoice/types', { axios.post('/api/preinvoice/docs/search')
type: 'sell'
}).then((response) => {
this.types = response.data;
});
axios.post('/api/presell/docs/search')
.then((response) => { .then((response) => {
this.items = response.data; this.items = response.data;
this.orgItems = response.data; this.orgItems = response.data;
@ -350,53 +282,59 @@ export default defineComponent ({
this.loading = false; this.loading = false;
}) })
}, },
showSnackbar(text, color = 'success') {
this.snackbar.text = text;
this.snackbar.color = color;
this.snackbar.show = true;
},
deleteItems() { deleteItems() {
if (this.itemsSelected.length == 0) { if (this.itemsSelected.length == 0) {
Swal.fire({ this.showSnackbar('لطفا حداقل یک پیش فاکتور را انتخاب کنید.', 'warning');
text: 'هیچ موردی انتخاب نشده است.', return;
icon: 'warning',
confirmButtonText: 'قبول'
});
} }
else { this.deleteGroupDialog = true;
Swal.fire({ },
text: 'آیا برای حذف این مورد مطمئن هستید؟ تمامی اسناد پرداخت و حواله های انبار همراه فاکتور نیز حذف خواهند شد.', confirmDeleteGroup() {
showCancelButton: true, this.loading = true;
confirmButtonText: 'بله', this.deleteGroupDialog = false;
cancelButtonText: `خیر`, axios.post('/api/preinvoice/remove/group', {
icon: 'warning' 'items': this.itemsSelected
}).then((result) => { }).then((response) => {
/* Read more about isConfirmed, isDenied below */ this.loading = false;
if (result.isConfirmed) { if (response.data.result == 1) {
this.loading = true; this.loadData();
axios.post('/api/accounting/remove/group', { this.showSnackbar('فاکتورها با موفقیت حذف شد.', 'success');
'items': this.itemsSelected }
else if (response.data.result == 2) {
this.showSnackbar(response.data.message, 'warning');
}
});
},
deleteItem(code) {
this.selectedCode = code;
this.deleteDialog = true;
},
confirmDelete() {
this.deleteDialog = false;
axios.post('/api/preinvoice/delete/' + this.selectedCode).then((response) => {
if (response.data.result == 1) {
let index = 0;
for (let z = 0; z < this.items.length; z++) {
index++;
if (this.items[z]['code'] == this.selectedCode) {
this.items.splice(index - 1, 1);
} }
).then((response) => {
this.loading = false;
if (response.data.result == 1) {
this.loadData();
Swal.fire({
text: 'فاکتور ها با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
});
}
else if (response.data.result == 2) {
Swal.fire({
text: response.data.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
}
})
} }
}) this.showSnackbar('فاکتور با موفقیت حذف شد.', 'success');
} }
else if (response.data.result == 2) {
this.showSnackbar(response.data.message, 'warning');
}
});
}, },
printInvoice(pdf = true, cloudePrinters = true) { printInvoice(pdf = true, cloudePrinters = true) {
this.loading = true; this.loading = true;
axios.post('/api/presell/print/invoice', { axios.post('/api/preinvoice/print/invoice', {
'code': this.printOptions.selectedPrintCode, 'code': this.printOptions.selectedPrintCode,
'pdf': pdf, 'pdf': pdf,
'printers': cloudePrinters, 'printers': cloudePrinters,
@ -406,45 +344,10 @@ export default defineComponent ({
window.open(this.$API_URL + '/front/print/' + response.data.id, '_blank', 'noreferrer'); window.open(this.$API_URL + '/front/print/' + response.data.id, '_blank', 'noreferrer');
}); });
}, },
deleteItem(code) { isNumeric(str) {
Swal.fire({ if (typeof str != "string") return false;
text: 'آیا برای حذف این مورد مطمئن هستید؟ تمامی اسناد پرداخت و حواله های انبار همراه فاکتور نیز حذف خواهند شد.', return !isNaN(str) && !isNaN(parseFloat(str));
showCancelButton: true, },
confirmButtonText: 'بله',
cancelButtonText: `خیر`,
icon: 'warning'
}).then((result) => {
/* Read more about isConfirmed, isDenied below */
if (result.isConfirmed) {
axios.post('/api/accounting/remove', {
'code': code
}
).then((response) => {
if (response.data.result == 1) {
let index = 0;
for (let z = 0; z < this.items.length; z++) {
index++;
if (this.items[z]['code'] == code) {
this.items.splice(index - 1, 1);
}
}
Swal.fire({
text: 'فاکتور فروش با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
});
}
else if (response.data.result == 2) {
Swal.fire({
text: response.data.message,
icon: 'warning',
confirmButtonText: 'قبول'
});
}
})
}
})
}
}, },
beforeMount() { beforeMount() {
this.loadData(); this.loadData();
@ -454,11 +357,11 @@ export default defineComponent ({
handler: function (val, oldVal) { handler: function (val, oldVal) {
this.sumSelected = 0; this.sumSelected = 0;
this.itemsSelected.forEach((item) => { this.itemsSelected.forEach((item) => {
if (typeof item.amount.valueOf() === "string") { if (typeof item.totalAmount.valueOf() === "string") {
this.sumSelected += parseInt(item.amount.replaceAll(",", "")) this.sumSelected += parseInt(item.totalAmount.replaceAll(",", ""))
} }
else { else {
this.sumSelected += item.amount; this.sumSelected += item.totalAmount;
} }
}); });
}, },
@ -470,26 +373,40 @@ export default defineComponent ({
this.items = this.orgItems; this.items = this.orgItems;
} }
else { else {
const searchTerm = this.searchValue.toLowerCase().replace(/,/g, '');
let temp = []; let temp = [];
this.orgItems.forEach((item) => { this.orgItems.forEach((item) => {
if (item.person.nikename.includes(this.searchValue)) { // جستجو در فیلدهای متنی
temp.push(item) const personFields = [
item.person?.nikename,
item.person?.name,
item.person?.family,
item.person?.mobile,
item.person?.phone,
item.person?.address
].filter(Boolean); // حذف مقادیر undefined
const hasPersonMatch = personFields.some(field =>
field.toLowerCase().includes(searchTerm)
);
if (hasPersonMatch ||
item.des.toLowerCase().includes(searchTerm) ||
item.code.includes(searchTerm)) {
temp.push(item);
} }
else if (item.date.includes(this.searchValue)) { // جستجو در فیلدهای عددی
temp.push(item) else if (this.isNumeric(searchTerm)) {
} const searchNumber = parseFloat(searchTerm);
else if (item.amount.toString().includes(this.searchValue)) { if (item.amount == searchNumber ||
temp.push(item) item.totalAmount == searchNumber ||
} item.discountAll == searchNumber ||
else if (item.des.includes(this.searchValue)) { item.transferCost == searchNumber ||
temp.push(item) item.amount.toString().includes(searchTerm) ||
} item.totalAmount.toString().includes(searchTerm) ||
else if (item.code.includes(this.searchValue)) { item.discountAll.toString().includes(searchTerm) ||
temp.push(item) item.transferCost.toString().includes(searchTerm)) {
} temp.push(item);
else if (item.label) {
if (item.label.label.includes(this.searchValue)) {
temp.push(item)
} }
} }
}); });
@ -502,4 +419,11 @@ export default defineComponent ({
}) })
</script> </script>
<style scoped></style> <style scoped>
.v-card {
border-radius: 8px;
}
.v-dialog {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
</style>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
<template>
<div>
<v-dialog v-model="dialog" fullscreen @update:model-value="handleDialogClose">
<v-card>
<v-toolbar color="toolbar" :title="$t('drawer.presell_view')">
<template v-slot:prepend>
<v-tooltip :text="$t('dialog.back')" location="bottom">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" @click="handleDialogClose(false)" class="d-none d-sm-flex" variant="text"
icon="mdi-arrow-right" />
</template>
</v-tooltip>
</template>
<v-spacer></v-spacer>
<v-tooltip :text="$t('dialog.export_pdf')" location="bottom">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" icon="mdi-file-pdf-box" color="primary" @click="printInvoice()"></v-btn>
</template>
</v-tooltip>
</v-toolbar>
<v-card-text>
<v-row>
<v-col cols="12" md="6">
<v-card class="mb-4" :title="$t('dialog.presell_info')">
<v-card-text>
<v-row>
<v-col cols="12" sm="6">
<v-text-field :model-value="presellData.code" :label="$t('dialog.code')" readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="presellData.date" :label="$t('dialog.date')" readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="presellData.person?.nikename" :label="$t('dialog.person')" readonly>
</v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="presellData.des" :label="$t('dialog.des')" readonly></v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card class="mb-4" :title="$t('dialog.financial_info')">
<v-card-text>
<v-row>
<v-col cols="12" sm="6">
<v-text-field :model-value="$filters.formatNumber(presellData.amount)" :label="$t('dialog.amount')"
readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="$filters.formatNumber(presellData.totalDiscount)"
:label="$t('dialog.discount')" readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="$filters.formatNumber(presellData.shippingCost)"
:label="$t('dialog.transfer_cost')" readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field :model-value="$filters.formatNumber(presellData.totalAmount)"
:label="$t('dialog.total_amount')" readonly></v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12">
<v-card :title="$t('dialog.invoice_items')">
<v-card-text>
<EasyDataTable table-class-name="customize-table" :headers="itemsHeaders" :items="presellData.items"
theme-color="#1d90ff" header-text-direction="center" body-text-direction="center"
rowsPerPageMessage="تعداد سطر" emptyMessage="اطلاعاتی برای نمایش وجود ندارد">
<template #item-count="{ count }">
{{ $filters.formatNumber(count) }}
</template>
<template #item-price="{ price }">
{{ $filters.formatNumber(price) }}
</template>
<template #item-discountAmount="{ discountAmount }">
{{ $filters.formatNumber(discountAmount) }}
</template>
<template #item-total="{ total }">
{{ $filters.formatNumber(total) }}
</template>
</EasyDataTable>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
import axios from "axios";
import { ref, defineComponent } from "vue";
export default defineComponent({
name: "PresellView",
props: {
code: {
type: [String, Number],
required: true
},
modelValue: {
type: Boolean,
default: true
}
},
data() {
return {
dialog: this.modelValue,
presellData: {},
itemsHeaders: [
{ text: "کد کالا", value: "itemCode" },
{ text: "نام کالا", value: "itemName" },
{ text: "تعداد", value: "count" },
{ text: "قیمت", value: "price" },
{ text: "مبلغ", value: "amount" },
{ text: "جمع", value: "total" }
],
printOptions: {
note: true,
bidInfo: true,
taxInfo: true,
discountInfo: true,
paper: 'A4-L'
}
}
},
methods: {
handleDialogClose(value) {
this.dialog = value;
this.$emit('update:modelValue', value);
this.$emit('close');
},
loadData() {
axios.post('/api/preinvoice/get/' + this.code)
.then((response) => {
this.presellData = response.data;
});
},
printInvoice() {
axios.post('/api/preinvoice/print/invoice', {
'code': this.code,
'pdf': true,
'printers': true,
'printOptions': this.printOptions
}).then((response) => {
window.open(this.$API_URL + '/front/print/' + response.data.id, '_blank', 'noreferrer');
});
}
},
created() {
this.loadData();
}
});
</script>
<style scoped>
.v-card {
border-radius: 8px;
}
</style>

File diff suppressed because it is too large Load diff