<?php
namespace Diplix\KMGBundle\Controller;
use Cocur\Slugify\Slugify;
use Diplix\Commons\DataHandlingBundle\Entity\SysLogEntry;
use Diplix\KMGBundle\DataTables\DataTablesHelper;
use Diplix\KMGBundle\DataTables\Expr\ExprStub;
use Diplix\KMGBundle\Entity\Accounting\CoopMember;
use Diplix\KMGBundle\Entity\Address;
use Diplix\KMGBundle\Entity\Customer;
use Diplix\KMGBundle\Entity\Order;
use Diplix\KMGBundle\Entity\OrderStatus;
use Diplix\KMGBundle\Entity\Platform\PlatformClient;
use Diplix\KMGBundle\Entity\PriceList;
use Diplix\KMGBundle\Entity\Role;
use Diplix\KMGBundle\Entity\User;
use Diplix\KMGBundle\Exception\OrderValidationException;
use Diplix\KMGBundle\Form\Admin\ChangeOrderCustomerForm;
use Diplix\KMGBundle\Form\CancelOrderForm;
use Diplix\KMGBundle\Form\Contraints\ValidOrder;
use Diplix\KMGBundle\Form\OrderForm;
use Diplix\KMGBundle\Form\OrderMinimalForm;
use Diplix\KMGBundle\Form\OrderStatusFilterForm;
use Diplix\KMGBundle\Helper\ClientConfigProvider;
use Diplix\KMGBundle\PdfGeneration\JobPdf;
use Diplix\KMGBundle\PriceCalculator\CalculatorService;
use Diplix\KMGBundle\Service\OrderHandler;
use Diplix\KMGBundle\Util\ExcelExportHelper;
use Diplix\KMGBundle\PriceCalculator\AbstractPriceCalculator;
use Diplix\KMGBundle\Repository\OrderRepository;
use Diplix\KMGBundle\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Google\Service\VersionHistory\Platform;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
use Symfony\Contracts\Translation\TranslatorInterface;
class OrderController extends BaseController
{
public static $days = ["So","Mo","Di","Mi","Do","Fr","Sa"];
public static $columnSetup = [
0=>array('fieldName' => 'ord.id', 'caption' => '#', 'headStyle' => 'width:16px;', 'type' =>DataTablesHelper::T_SELECTOR),
1=>array('fieldName' => 'ord.orderTime' , 'caption'=>'Datum'),
2=>array('fieldName' => '_orderToD' , 'caption'=>'Uhrzeit', 'virtual'=>true, 'sortable'=>false),
3=>array('fieldName' => 'ord.orderStatus', 'caption'=>'Status', 'virtual'=>true),
4=>array('fieldName' => '_route', 'caption'=>'Adresse(n)', 'virtual'=>true,'sortable'=>false),
5=>array('fieldName' => 'ord.beOwner','caption'=>'Besteller','virtual'=>true),
6=>array('fieldName' => 'ord.orderId', 'caption' => 'Auftrag', 'searchable'=>true,'visible'=>false),
7=>array('fieldName' => '_orderStatusId', 'caption'=>'Status', 'visible'=>false,'searchable'=>false),
8=>array('fieldName' => '_actions', 'headStyle'=>'width:92px;', 'type'=> DataTablesHelper::T_BUTTONS, 'caption' => ''),
9=>array('fieldName' => 'ord.isVip', 'caption'=>'VIP', 'visible'=>false),
10=>array('fieldName' => '_parentOrderId', 'caption'=>'ParentOrderId', 'visible'=>false,'searchable'=>false),
11=>array('fieldName' => '_visual', 'caption'=>'', 'virtual'=>true,'visible'=>false,'searchable'=>false),
];
protected function getDth()
{
return new DataTablesHelper(self::$columnSetup,
[
'ajaxUrl' => $this->get('router')->generate('orders-data'),
'ajaxData' => 'requestData',
'searching' => true,
//'deferLoading' => true,
'defaultSorting' => array(1 => 'desc')
]);
}
/**
* @var OrderRepository
*/
protected $orderRepo;
/**
* @var UserRepository
*/
protected $userRepo;
/** @var Customer */
protected $customerContext;
public function __construct(
protected ClientConfigProvider $ccp,
protected OrderHandler $orderHandler,
protected CalculatorService $calculatorService,
protected TranslatorInterface $translator,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
)
{
}
protected function init(Request $request)
{
$this->orderRepo = $this->managerRegistry->getRepository(Order::class);
$this->userRepo = $this->managerRegistry->getRepository(User::class);
if (!is_object($this->getCurrentUser()->getCustomer())) throw new \Exception("User has no assigned customer");
$this->customerContext = $this->getCustomerContextFromSession();
// check pw age
$this->checkPasswordAge();
}
/**
* @return \Diplix\KMGBundle\Entity\Customer
*/
protected function getCustomerContext()
{
return $this->customerContext;
}
public static function addMonths($date, $count)
{
$m = $date->format("m");
$y = $date->format("Y");
$m += $count;
if ($m < 1) // move into last year (only works for max 12 months into the past
{
$m = 12 + $m;
$y--;
}
$y += ($m / 12);
$m = max(1, ($m % 12));
return new \DateTime(sprintf("%d-%d-01", $y, $m));
}
protected function export($list)
{
$tempFn = $this->getParameter("dx.temp_dir") . uniqid("export") . ".xlsx";
$xh = new ExcelExportHelper();
$xh->setSingleSheetMode("Bestellungen");
/** @var array $row */
foreach ($list as $row) {
$status = ($row["remoteStatus"] == Order::REMOTE_PENDING ? "Entwurf" : $row["orderStatus"]["name"]);
$data = array(
"AuftragsNr" => array($row["orderId"], ExcelExportHelper::TYPE_STRING),
"Zeitpunkt" => array($row["orderTime"], ExcelExportHelper::XTYPE_DATE),
"Personen" => array($row["personCount"], ExcelExportHelper::TYPE_NUMERIC),
"Status" => array($status, ExcelExportHelper::TYPE_STRING),
"KSt." => array($row["costCenter"], ExcelExportHelper::TYPE_STRING),
"Kommentar" => array($row["comment"], ExcelExportHelper::TYPE_STRING),
"Besteller" => array($row["ordererForename"] . " " . $row["ordererName"], ExcelExportHelper::TYPE_STRING),
"Best.Email" => array($row["ordererMail"], ExcelExportHelper::TYPE_STRING),
"Best.Tel." => array($row["ordererPhone"], ExcelExportHelper::TYPE_STRING),
"Kindersitze" => array($row["childSeatCount"], ExcelExportHelper::TYPE_NUMERIC),
"KiSiInfo" => array($row["childSeatInfo"], ExcelExportHelper::TYPE_STRING),
"Fhzg" => array($row["carType"], ExcelExportHelper::TYPE_STRING),
"GeschätzteDistanz" => array($row["lastEstimatedDistance"], ExcelExportHelper::TYPE_NUMERIC),
"GeschätzerPreis" => array($row["lastEstimatedPrice"], ExcelExportHelper::TYPE_NUMERIC),
"PlId" => array($row["lastPriceListId"], ExcelExportHelper::TYPE_NUMERIC),
);
$cnt = 0;
foreach ($row["addressList"] as $a) {
$cnt++;
$data["A" . $cnt . " Name"] = array($a["name"], ExcelExportHelper::TYPE_STRING);
$data["A" . $cnt . " Fahrgast"] = array($a["passenger"], ExcelExportHelper::TYPE_STRING);
$data["A" . $cnt . " Strasse"] = array($a["street"], ExcelExportHelper::TYPE_STRING);
$data["A" . $cnt . " HNr"] = array($a["number"], ExcelExportHelper::TYPE_STRING);
$data["A" . $cnt . " PLZ"] = array($a["zipCode"], ExcelExportHelper::TYPE_STRING);
$data["A" . $cnt . " Stadt"] = array($a["city"], ExcelExportHelper::TYPE_STRING);
}
$xh->addRow($data);
}
$outFn = "bestellungen_" . date("Y-m-d_H-i") . "." . $xh->getExtension();
$xh->saveTo($tempFn);
$resp = new BinaryFileResponse($tempFn);
$resp->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$outFn
);
return $resp;
}
public function indexAction(Request $request)
{
$this->init($request);
$printView = $request->query->get("print", false);
$from = $this->addMonths(new \DateTime(), -3);
$to = $this->addMonths($from, 6);
$filter = (object)array("filterStatus" => $request->query->get("filterStatus", 0),
"filterFrom" => $from,
"filterTo" => $to,
);
$form = $this->createForm(OrderStatusFilterForm::class, $filter, [
OrderStatusFilterForm::OPT_EM => $this->managerRegistry->getManager()]);
$form->handleRequest($request);
$list = null;
if ($form->get('exportBtn')->isClicked() || $printView)
{
[$filterUserList, $filterCustomerId] = $this->getUserAndCustomerFilter();
$realFilterStatus = $filter->filterStatus;
/** @noinspection TypeUnsafeComparisonInspection */
if ($realFilterStatus==OrderStatus::STATUS_OPEN) // always show drafts along the open rides
{
$realFilterStatus = [OrderStatus::STATUS_OPEN, OrderStatus::STATUS_DRAFT];
}
$list = $this->orderRepo->findAllForOverview(
$filterUserList,
$filterCustomerId,
$realFilterStatus,
$filter->filterFrom,
$filter->filterTo);
}
if ($form->get('exportBtn')->isClicked())
{
return $this->export($list);
}
return $this->render(
($printView ? "@DiplixKMG/Orders/list-print.html.twig" : "@DiplixKMG/Orders/list.html.twig"),
[
"config" => $this->ccp,
"list" => $list,
"form" => $form->createView(),
"calendar" => $this->fetchCalendarInfo(new \DateTime("-2 year"), new \DateTime("+1 year")),
'columnSetup' =>$this->getDth()->getColumnSetup()
]);
}
protected function g2i($v)
{
$parts = explode(".", $v);
return sprintf("%4d-%02d-%02d", $parts[2], $parts[1], $parts[0]);
}
protected function getUserAndCustomerFilter()
{
$filterUserList = $this->userRepo->findUsersISubstitute($this->getCurrentUser());
$filterUserList[] = $this->getCurrentUser();
$filterCustomerId = 0;
// super-admin is allowed to see all - but only sees what has been selected as context
if (
($this->hasUserRole(Role::SUPER_ADMIN))
||
($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN))
)
{
$filterUserList = null;
$filterCustomerId = $this->getCustomerContext()->getId();
}
else
{
// an order admin may see all orders from the same customer - his customer is stored in the session
if ($this->hasUserRole(Role::SUB_ORDER_ADMIN)) {
$filterUserList = null;
$filterCustomerId = $this->getCustomerContext()->getId();
}
}
return [$filterUserList, $filterCustomerId];
}
protected function fetchDataQueryPlain(Request $request)
{
$req = $request->query->all();
[$filterUserList, $filterCustomerId] = $this->getUserAndCustomerFilter();
$realFilterStatus = (int)$req['filterStatus'];
/** @noinspection TypeUnsafeComparisonInspection */
if ($realFilterStatus==OrderStatus::STATUS_OPEN) // always show drafts along the open rides
{
$realFilterStatus = [OrderStatus::STATUS_OPEN, OrderStatus::STATUS_DRAFT];
}
else if ($realFilterStatus!==0)
{
$realFilterStatus = [$realFilterStatus];
}
else
{
$realFilterStatus = null;
}
$from = new \DateTime($req["filterFrom"] . ' 00:00');
$to = new \DateTime($req["filterTo"]. ' 23:59');
/////////////////////////////////////////////
$dth = $this->getDth();
/** @var EntityRepository $repo */
$repo = $this->managerRegistry->getRepository(Order::class);
$cb = $repo->createQueryBuilder('ord')
->select('ord','cust','status','address','payment','assignedto')
->leftJoin('ord.customer','cust')
->leftJoin('ord.orderStatus','status')
->leftJoin('ord.addressList','address')
->leftJoin('ord.paymentReference','payment')
->leftJoin('ord.assignedTo','assignedto')
;
// process filtering,ordering,paging
$dth->setQueryBuilder($cb);
$rawOrder = $dth->getRawColumnOrder($req);
$dth->addSimpleFilter(1,$req,$rawOrder,[ ExprStub::like('ord.orderTime') ], 'ord.orderTime',true);
//$dth->addSimpleFilter(3,$req,$rawOrder,[ExprStub::like('status.name')], 'status.name',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.name') ], 'address.name',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.passenger') ], 'address.passenger',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.street') ], 'address.street',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.zipCode') ], 'address.zipCode',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.city') ], 'address.city',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.mobileNumber') ], 'address.mobileNumber',true);
$dth->addSimpleFilter(-1,$req,$rawOrder,[ ExprStub::like('address.email') ], 'address.email',true);
if ($request->query->has('columns'))
{
// if there is no 'columns' key we suspect a call made outside of datatables.js
$dth->processRequest($request);
}
// additional filters
if ( (is_array($filterUserList)) && (count($filterUserList)>0) )
{
$cb->andWhere($cb->expr()->in('ord.beOwner',':oid'))
->setParameter('oid',$filterUserList);
}
if ($filterCustomerId > 0)
{
$cb->andWhere('ord.customer = :cid')->setParameter('cid',$filterCustomerId);
}
if($realFilterStatus!==null)
{
$cb->andWhere($cb->expr()->in('ord.orderStatus',':status'))
->setParameter('status',$realFilterStatus);
}
$cb->andWhere('ord.orderTime > :from')->setParameter('from',$from);
$cb->andWhere('ord.orderTime <= :to')->setParameter('to',$to);
// query and compile paged data
$query = $cb->getQuery();
$query->setHydrationMode(AbstractQuery::HYDRATE_OBJECT);
return $query;
}
protected function fetchCalendarInfo($from,$until)
{
[$filterUserList, $filterCustomerId] = $this->getUserAndCustomerFilter();
$repo = $this->managerRegistry->getRepository(Order::class);
$calendar = array(
"start" => $from->format("Y-m-d"),
"end" => $until->format("Y-m-d"),
"data" => $repo->countByDay($from,$until,$filterUserList,$filterCustomerId)
);
return $calendar;
}
public function jsonDataAction(Request $request)
{
$this->init($request);
$trans = $this->translator;
$repo = $this->managerRegistry->getRepository(Order::class);
$query = $this->fetchDataQueryPlain($request);
$paginator = new Paginator($query,true);
$filteredCount = $paginator->count();
$filteredAndPaged = array();
$dit = $paginator->getIterator();
/** @var Order $row */
foreach ($dit as $row)
{
$allowEdit = ($row->getOrderStatus()->getId() == OrderStatus::STATUS_DRAFT)
||
($row->getRemoteStatus() == Order::REMOTE_PENDING)
||
(
(!$this->IsLagTooLow($row->getOrderTime()))
&&
($row->getOrderStatus()->getId() == OrderStatus::STATUS_OPEN)
);
$allowEdit = $allowEdit && (is_null($row->getReferencedParentOrder()));
$actions = [];
if ($allowEdit)
{
$actions[]= [
'route' => 'orders-edit',
'title' => 'Bearbeiten',
'icon' => 'fa fa-pencil',
'class' => 'btn-sm',
'parameters' =>["id"=>$row->getId()]
];
$actions[]= [
'route' => 'orders-cancel',
'title' => 'Stornieren',
'icon' => 'fa fa-times',
'class' => 'btn-sm',
'parameters' =>["id"=>$row->getId()]
];
}
else
{
$actions[]= [
'route' => 'orders-edit',
'title' => 'Bearbeiten',
'icon' => 'fa fa-eye',
'class' => 'btn-sm',
'parameters' =>["id"=>$row->getId()]
];
}
if ($row->getReferencedParentOrder()!==null)
{
$actions[]= [
'route' => 'orders-edit',
'title' => 'Zu grunde liegende Bestellung',
'icon' => 'fa fa-link',
'class' => 'btn-sm',
'parameters' =>["id"=>$row->getReferencedParentOrder()->getId()]
];
}
$actions[]= [
'route' => 'orders-new',
'title' => 'Als Vorlage verwenden',
'icon' => 'fa fa-clone',
'class' => 'btn-sm',
'parameters' =>array("template"=>$row->getId())
];
$route = [];
/** @var Address $a */
foreach ($row->getAddressList() as $a)
{
$r = '';
if ($a->getPassenger()!=='')
{
$r .= sprintf('<i>%s</i><br>',$a->getPassenger());
}
$r .= sprintf('%s, %s %s, %s',$a->getName(),$a->getStreet(),$a->getNumber(),$a->getCity());
$route[]=$r;
}
$route = implode('<br>',$route);
$visualOptions =[];
if ($row->getIsVip())
{
$visualOptions[]=[
'icon'=> 'fa fa-star',
'title'=> $trans->trans("VIP")
];
}
$wd = $row->getOrderTime() !== null ? self::$days[ $row->getOrderTime()->format('w') ] : '';
$one = [
0 => $row->getId(),
1 => $wd.' '.$row->getOrderTime()->format('d.m.Y'),
2 => $row->getOrderTime()->format('H:i'),
3 => $trans->trans($row->getOrderStatus()->getName(),[],'entities'),
4 => $route,
5 => $row->getBeOwner()->getUsername(),
6 => $row->getOrderId(),
7 => $row->getOrderStatus()->getId(),
8 => $actions,
9 => $row->getIsVip(),
10 => $row->getReferencedParentOrder()!== null ? $row->getReferencedParentOrder()->getId() : null,
11 => $visualOptions
];
$filteredAndPaged[]= $one;
}
// get total unfiltered count
$query = $repo->createQueryBuilder('ord')
->select('COUNT(ord.id)')
->getQuery();
$res = $query->getResult();
$totalRecords = (int)$res[0][1];
// compile output
$output = array(
'draw' => $req['draw'] ?? 0, // 'draw' only available if request is made through datatables.js
'recordsTotal' =>$totalRecords,
'recordsFiltered' =>$filteredCount,
'data' => $filteredAndPaged
);
return $this->getJsonResponse($request,$output);
}
protected function IsLagTooLow(\DateTime $dt)
{
return false; // TODO: re-enable temporarily removed check
// a user without time restriction is not bound to normal lag restrictions or any time restriction
if ($this->hasUserRole(Role::NO_TIME_RESTRICTION, true)) return false;
// all other users are limited
$earliest = new \DateTime();
$earliest->add(new \DateInterval(sprintf("PT%dH", Order::TIME_LAG_HOURS)));
return ($dt < $earliest);
}
public function editTimeOnlyAction(Request $request, $id, OrderHandler $oh)
{
throw new AccessDeniedException("not yet enbaled");
$this->init($request);
$this->denyAccessUnlessGranted(Role::DISPO);
$returnUrl = $request->get('returnTo',$this->generateUrl('orders'));
// record
/** @var Order $row */
$row = $this->orderRepo->findOneBy(array("id" => $id));
if ($row===null) {
$this->addFlash('warning', 'order.invalid-id');
return $this->redirect($returnUrl);
}
// context
if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId())
{
if (($this->hasUserRole(Role::DISPO))&&($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) )
{
$this->addFlash('warning','Benutzerkontext wurde automatisch gewechselt.');
return $this->redirectToRoute('admin-switch-customer',[
'customerId'=>$row->getCustomer()->getId(),
'referTo'=>$this->generateUrl('orders-edit',['id'=>$row->getId()])]);
}
if (!$this->hasUserRole(Role::SUPER_ADMIN))
{
$this->addFlash('warning', 'order.invalid-id');
return $this->redirect($returnUrl);
}
$this->addFlash("warning", sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
$row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
}
$editable = true;
if ($row->getOrderStatus()->getId()!== OrderStatus::STATUS_OPEN)
{
if (!$this->hasUserRole(Role::EDIT_CLOSED_ORDERS, true))
{
$editable = false;
}
$this->addFlash("info", sprintf("Hinweis: Sie bearbeiten einen Auftrag der bereits %s ist",$row->getOrderStatus()->getName()));
}
else // if open
{
if ($this->IsLagTooLow($row->getOrderTime())) { // also does permission check
$this->addFlash('warning', 'order.edit-not-possible-lag');
$editable = false;
}
}
if ($row->getReferencedParentOrder() === null)
{
$this->addFlash('warning', 'Diese Funktion steht für Hauptaufträge nicht zur Verfügung.');
$editable = false;
}
if ($row->getJob()!== null && $row->getJob()->isApprovedByAccounting())
{
$this->addFlash('warning','Diese Fahrt wurde bereits von der Buchhaltung freigegeben.');
$editable = false;
}
// form
$opts = array();
if (!$editable) {
$opts["disabled"] = true;
}
$opts[OrderForm::OPT_CUSTOMER] = $row->getCustomer();
$opts[OrderForm::additionalBlumFields] = $this->getCustomerContext()->getShowAdditionalBlumFields();
$opts[OrderForm::SHOW_INTERNAL_COMMENT] = $this->hasUserRole(Role::DISPO);
$opts['constraints'] = [ new ValidOrder([
'lagTimeInHours' => $this->hasUserRole(Role::NO_TIME_RESTRICTION, true) ? 0 : Order::TIME_LAG_HOURS
])];
$form = $this->createForm(OrderMinimalForm::class, $row, $opts);
if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
//$form->get("autoTime")->setData(true);
}
$form->handleRequest($request);
if ($form->isSubmitted())
{
if (!$editable) {
$this->addFlash("danger", "message.access-denied");
return $this->redirectToRoute("orders-edit", array("id" => $id, 'returnTo' => $returnUrl));
}
if ($form->isValid())
{
try
{
$oh->storeOrUpdate($row);
$oh->initiateOrder($row,true);
}
catch (OrderValidationException $ex) {
$form->addError(new FormError($ex->getMessage()));
}
if (($form->isValid()) && ($form->getErrors()->count() < 1)) {
if ($row->getOrderTime()->format('Y-m-d H:i:s') < (new \DateTime())->format('Y-m-d H:i:s')) {
$this->addFlash("warning", "Achtung. Fahrt liegt in der Vergangenheit");
}
$this->addFlash("success", "order.saved");
return $this->redirect($returnUrl);
}
}
$this->addFlash("warning", "order.please-check-input");
}
return $this->render("@DiplixKMG/Orders/edit-time-only.html.twig", [
"form" => $form->createView(),
"order" => $row,
"returnTo" => $returnUrl
]);
}
public function editAction(Request $request, $id)
{
$this->init($request);
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$oh = $this->orderHandler;
$returnUrl = $request->get('returnTo',$this->generateUrl('orders'));
$editable = true;
$userList = $this->userRepo->findUsersISubstitute($this->getCurrentUser());
$userList[] = $this->getCurrentUser();
if ($this->hasUserRole(Role::ORDER_CHANGE_OWNER, true)) {
$alreadyInList = array();
foreach ($userList as $one) $alreadyInList[] = $one->getId();
$cu = $this->userRepo->findUsersOfCustomer($this->getCustomerContext());
foreach ($cu as $one) {
if (!in_array($one->getId(), $alreadyInList))
$userList[] = $one;
}
// todo: add current owner to list to prevent change by accident (e.g. vertretung eingerichtet)
}
$sortedPriceLists = array();
$customerPriceLists = $this->getCustomerContext()->getPriceLists()->toArray();
$customerPriceLists = array_filter($customerPriceLists,function (PriceList $pl){
return !$pl->isHidden();
});
$all = "PKW/VAN";
if (!is_null($this->getCustomerContext()->getDefaultPriceList())) {
if (count($customerPriceLists) > 1) {
// move the default pricelist to the top of the list
$pl = $this->getCustomerContext()->getDefaultPriceList();
/*
$key = array_search($pl, $customerPriceLists, true);
$customerPriceLists = array_merge(array($key=>$customerPriceLists[$key]), $customerPriceLists);
*/
$temp = $customerPriceLists;
$customerPriceLists = [];
$customerPriceLists[] = $pl;
$plId = $pl->getId();
foreach ($temp as $t) {
if ($t->getId() != $plId) $customerPriceLists[] = $t;
}
}
}
/** @var PriceList $pl */
foreach ($customerPriceLists as $pl) {
$ct = $pl->getCarTypeFilter();
if (count($ct??[]) < 1) {
$sortedPriceLists[$all] [] = $pl;
} else {
$key = join("/", $ct);
$sortedPriceLists[$key] [] = $pl;
}
}
$opts = array();
/** @var $row Order */
if ($id > 0) {
$row = $this->orderRepo->findOneBy(array("id" => $id));
if (!is_object($row)) {
$this->addFlash('warning', 'order.invalid-id');
return $this->redirect($returnUrl);
}
if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId()) {
if (($this->hasUserRole(Role::DISPO))&&($this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) )
{
$this->addFlash('warning','Benutzerkontext wurde automatisch gewechselt.');
return $this->redirectToRoute('admin-switch-customer',[
'customerId'=>$row->getCustomer()->getId(),
'referTo'=>$this->generateUrl('orders-edit',['id'=>$row->getId()])]);
}
else
if (!$this->hasUserRole(Role::SUPER_ADMIN))
{
$this->addFlash('warning', 'order.invalid-id');
return $this->redirect($returnUrl);
}
else
{
$this->addFlash("warning", sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
$row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
}
}
if (!is_null($row->getReferencedParentOrder())) {
$this->addFlash("info", sprintf("Diese Fahrt wurde automatisch angelegt."));
$editable = false;
}
if ($row->getOrderStatus()->getId() === OrderStatus::STATUS_DRAFT)
{
$this->addFlash('info','Sie bearbeiten einen Entwurf');
}
else
if ($row->getOrderStatus()->getId()!== OrderStatus::STATUS_OPEN)
{
if (!$this->hasUserRole(Role::EDIT_CLOSED_ORDERS, true))
{
$editable = false;
}
$this->addFlash("info", sprintf("Hinweis: Sie bearbeiten einen Auftrag der bereits %s ist",$row->getOrderStatus()->getName()));
}
else // if open
{
if ($this->IsLagTooLow($row->getOrderTime())) { // also does permission check
$this->addFlash('warning', 'order.edit-not-possible-lag');
$editable = false;
}
}
if ($row->getJob()!== null && $row->getJob()->isApprovedByAccounting())
{
$this->addFlash('warning','Diese Fahrt wurde bereits von der Buchhaltung freigegeben.');
$editable = false;
}
}
else
{
$opts[OrderForm::OPT_IS_NEW] = true;
$u = $this->managerRegistry->getManager()->find(User::class, $this->getCurrentUser()->getId());
assert($u!==null);
$customer = $this->managerRegistry->getManager()->getReference(Customer::class, $this->getCustomerContext()->getId());
//$childRow = null;
$tmplId = $request->query->get('template', 0);
$row = null;
if ($tmplId > 0)
{
$original = $this->orderRepo->findOneBy(array('id' => $tmplId));
$this->managerRegistry->getManager()->detach($original);
foreach ($original->getAddressList() as $a)
{
$this->managerRegistry->getManager()->detach($a);
}
$row = $original!==null ? clone $original : null;
}
if ($row === null)
{
$row = $oh->getNewOrder($u,$customer,OrderStatus::STATUS_DRAFT,null,$u->getAutoFillOrdererDetailsInNewOrder());
// BLUM extras
if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
// remove second address from empty order
$row->removeAddress($row->getAddressList()->last());
$row->setDestination("FRA");
// enforce that fields are filled out consciously
$row->setChildSeatCount(null);
$row->setPersonAsChildrenCount(null);
$row->setPersonCount(null);
$row->setExtraLuggage(null);
}
}
$row->setBeOwner($u);
$row->setOrderStatus($this->managerRegistry->getManager()->getReference(OrderStatus::class, OrderStatus::STATUS_DRAFT));
}
$history = $this->orderRepo->findHistoryFor($row);
// keep original addresses to correctly remove them from the database if the user had removed them in the form
$originalAddresses = new ArrayCollection();
foreach ($row->getAddressList() as $a) {
$originalAddresses->add($a);
}
if (!$editable) {
$opts["disabled"] = true;
}
if (count($userList) > 1) { // only show selection field when there are more users to select than only me
$opts[OrderForm::OPT_SELECTABLE_OWNER_LIST] = $userList;
}
$opts[OrderForm::OPT_CUSTOMER] = $row->getCustomer();
$opts[OrderForm::allowFixedPrice] = $this->hasUserRole(Role::GLOBAL_ORDER_ADMIN);
$opts[OrderForm::additionalBlumFields] = $this->getCustomerContext()->getShowAdditionalBlumFields();
$opts[OrderForm::em] = $this->managerRegistry->getManager();
$opts[OrderForm::allowInstantPayment] = $this->getCustomerContext()->getAllowInstantPayment();
$opts[OrderForm::ALLOW_SUPPRESSION_OF_MAIL_CONFIRMATION] = $this->hasUserRole(Role::GLOBAL_ORDER_ADMIN);
$opts[OrderForm::SHOW_INTERNAL_COMMENT] = $this->hasUserRole(Role::DISPO);
$opts['constraints'] = [ new ValidOrder([
'lagTimeInHours' => $this->hasUserRole(Role::NO_TIME_RESTRICTION, true) ? 0 : Order::TIME_LAG_HOURS
])];
$availCarTypes = Order::$carTypes;
foreach ($this->ccp->getCarTypePersons() as $type=>$max)
{
if ((isset($availCarTypes[$type])) && ($max<1) )
{
unset($availCarTypes[$type]);
}
}
$opts[OrderForm::OPT_CAR_TYPES] = $availCarTypes;
$opts[OrderForm::OPT_DISCOUNT_OPTIONS] = $this->getCustomerContext()->getDiscountOptions();
$allowedCategories = $this->getCurrentUser()->getDashboards();
// if no dashboard is configured for the user -> default is allowed
if (empty($allowedCategories))
{
$allowedCategories[] = PlatformClient::DEFAULT_DASHBOARD;
}
$opts[OrderForm::OPT_DASHBOARDS] = $allowedCategories;
$form = $this->createForm(OrderForm::class, $row, $opts);
if ($this->getCustomerContext()->getShowAdditionalBlumFields()) {
$form->get("autoTime")->setData(true);
}
if (($opts[OrderForm::OPT_IS_NEW]??false) && count($allowedCategories) === 1)
{
$form->get('dashboard_category')->setData(reset($allowedCategories));
}
$form->handleRequest($request);
if ($form->isSubmitted())
{
if (!$editable) {
$this->addFlash("danger", "message.access-denied");
return $this->redirectToRoute("orders-edit", array("id" => $id, 'returnTo'=>$returnUrl));
}
if ($form->has('merkblattGelesen') && ($form->get('merkblattGelesen')->getData() !== true)) {
$form->get('merkblattGelesen')->addError(new FormError('Bitte bestätigen Sie, dass Sie das Merkblatt gelesen haben.'));
}
if ($form->has('dashboard_category'))
{
$cat = $form->get('dashboard_category')->getData();
if (!in_array($cat, $allowedCategories, true))
{
$form->get('dashboard_category')->addError(new FormError("Die wählen Sie nachfolgend eine Kategorie. Erlaubt:".implode(",",$allowedCategories)));
}
else
{
if ($cat === PlatformClient::DEFAULT_DASHBOARD)
{
$cat = null;
}
$row->setDispatchCategory($cat);
}
}
/** @noinspection MissingOrEmptyGroupStatementInspection */
else
{
// otherwise we assume default=null when creating a new ride
// or do not change it when editing
}
// proceed
if ($form->isValid()) {
// clear order reference for removed addresses
/** @var Address $a */
foreach ($originalAddresses as $a) {
if (false === $row->getAddressList()->contains($a)) {
$a->setOwningOrder(null);
}
}
// try to store the order
try {
$oh->storeOrUpdate($row);
} catch (OrderValidationException $ex) {
$form->addError(new FormError($ex->getMessage()));
}
}
// //// test
// $al = $row->getAddressList()->toArray();
// reset($al);
// /**
// * @var Address $e
// */
// foreach ($al as $k=>$e)
// {
// $this->addFlash('notice',implode(',',[
// $k,
// $e->getName(),
// $e->getZipCode()
// ]));
// }
//
// /// Test
try {
// since errors may be emitted above we check again
if (($form->isValid()) && ($form->getErrors()->count() < 1)) {
if ($form->get('submit_order')->isClicked()) {
$suppresssMail = $form->has('suppress_mail') && ($form->get('suppress_mail')->getData() == true);
$oh->initiateOrder($row,$suppresssMail);
if ($row->getChildOrder() !== null) {
$oh->initiateOrder($row->getChildOrder(),$suppresssMail);
}
if ($row->getOrderTime()->format('Y-m-d H:i:s') < (new \DateTime())->format('Y-m-d H:i:s')) {
$this->addFlash("warning", "Achtung. Fahrt liegt in der Vergangenheit");
}
$this->addFlash('success', 'Bestellung wurde übermittelt.');
if ($suppresssMail)
{
$this->addFlash('warning', 'Es wurde keine eMail-Bestätigung versandt.');
}
if (($form->has('payNow')) && ($form->get('payNow')->getData() === true)) {
return $this->redirectToRoute("payment-start", ["orderId" => $row->getId()]);
}
} else {
$this->addFlash("success", "order.saved");
}
return $this->redirect($returnUrl);
}
}
catch (\Throwable $ex)
{
$this->addFlash("danger", $ex->getMessage() .':' . $ex->getLine().':'. $ex->getFile().':'. $ex->getTraceAsString());
}
$this->addFlash("warning", "order.please-check-input");
}
$this->ccp->getCarTypePersons();
$carPersonsMax = $this->ccp->getCarTypePersons();
if ($this->getCustomerContext()->getShowAdditionalBlumFields())
{
$carPersonsMax = array(
Order::CARTYPE_PKW => 3,
Order::CARTYPE_KOMBI => 4,
Order::CARTYPE_MINIVAN =>6,
Order::CARTYPE_BUS=>54);
}
return $this->render("@DiplixKMG/Orders/edit.html.twig", array(
"row" => $row,
"config" => $this->ccp,
"returnUrl"=>$returnUrl,
"history" => $history,
"historyMessageMap" => SysLogEntry::$messageMap,
"form" => $form->createView(),
"editable" => $editable,
"sortedPriceLists" => $sortedPriceLists,
"carTypePersonsMax" => $carPersonsMax,
"user" => $this->getCurrentUser(),
"customerContext" => $this->getCustomerContext()
));
}
public function cancelAction(Request $request, $id)
{
$returnUrl = $request->get('returnTo',$this->generateUrl('orders'));
$this->init($request);
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$oh = $this->orderHandler;
try {
/** @var Order $row */
$row = $this->orderRepo->findOneBy(array("id" => $id));
// check context
if ($row->getCustomer()->getId() !== $this->getCustomerContext()->getId())
{
if (!$this->hasUserRole(Role::SUPER_ADMIN))
{
throw new OrderValidationException('order.invalid-id');
}
$this->addFlash("warning", sprintf("Achtung!: Diese Bestellung (cc#%d) gehört nicht dem ausgewählten Kundenkontext (cc#%d) !",
$row->getCustomer()->getId(), $this->getCustomerContext()->getId()));
}
$oh->ensureThatOrderCanBeCancelled($row);
$form = $this->createForm(CancelOrderForm::class, array("id" => $id));
$form->handleRequest($request);
if ( ($form->isSubmitted()) && ($form->get('commit')->getData() == 1) ) {
$oh->cancelOrder($row);
$this->addFlash('success', 'order.canceled');
return $this->redirect($returnUrl);
}
return $this->render('@DiplixKMG/Orders/cancel.html.twig', array(
"returnUrl"=>$returnUrl,
"row" => $row,
"form" => $form->createView())
);
}
catch (\Throwable $ex)
{
// throw $ex;
$this->addFlash('warning', $ex->getMessage());
return $this->redirect($returnUrl);
}
}
public function estimateDistanceAction(Request $request)
{
$this->init($request);
$ll = [];
try {
$req = array_merge($request->request->all(), $request->query->all());
$priceList = null;
if ($this->hasUserRole(Role::SUPER_ADMIN)) {
// admin may see all
$priceList = $this->managerRegistry->getManager()->find(PriceList::class, (int)$req['priceListId']);
} else {
// other users, filter only for lists available to the specific customer
$pl = $this->getCustomerContext()->getPriceLists();
foreach ($pl as $p) {
$ll[] = $p->getId();
/** @noinspection TypeUnsafeComparisonInspection */
if ($p->getId() == $req['priceListId']) {
$priceList = $p;
break;
}
}
}
if (!is_object($priceList)) {
throw new \Exception(sprintf('Pricelist #%d is not available ! (%s)', $req['priceListId'], implode(',', $ll)));
}
if (!isset($req['order']['addressList'])) throw new \Exception('No addresses');
$calc = $this->calculatorService;
$out = [
'status' => 'OK',
'message' => ''
];
$out['price'] = $calc->estimatePrice(
$priceList,
$req['order']['addressList'],
$req['order'],
null,
$this->getCustomerContext());
$out['distances'] = $calc->getDistances();
$out['distanceSum'] = $calc->getDistanceSumInKm($calc->getDistances());
$out['priceListId'] = $priceList->getId();
$out['priceMatrix'] = $calc->getPricingTableForDisplay();
$out['priceDisplayIndex'] = $calc->getDisplayIndex();
if ($this->ccp->isHidePricesForCustomer())
{
$out['price'] = 0;
$out['priceMatrix'] = [];
}
// and we're done
return $this->getJsonResponse($request, $out);
} catch (MissingOptionsException $ex)
{
return $this->getJsonResponse($request, array('status' => 'Fehlende Angaben', 'message' => 'Bitte korrigieren Sie den Auftrag oder wählen Sie eine unterstützte Preisliste', 'trace' => $ex->getTraceAsString()));
}
catch (\Exception $ex)
{
return $this->getJsonResponse($request, array(
'context' => isset($calc) ? $calc->getContext() : [],
'status' => 'EXCEPTION',
'message' => $ex->getMessage(),
"trace" => $ex->getTraceAsString()));
}
}
public function specialAction(Request $request)
{
return $this->render("@DiplixKMG/Orders/special.html.twig");
}
public function addressSuggestionsAction(Request $request)
{
$this->init($request);
$rep = $this->managerRegistry->getManager()->getRepository(Address::class);
$all = $rep->findForUser($this->getCurrentUser(), $this->getCustomerContext());
return $this->getJsonResponse($request, $all);
}
public function pickAddressAction(Request $request, $category, $target)
{
$this->init($request);
$rep = $this->managerRegistry->getManager()->getRepository(Address::class);
$all = $rep->findForUser($this->getCurrentUser(), $this->getCustomerContext(), $category);
return $this->render("@DiplixKMG/Orders/pick-address.html.twig", array("list" => $all, "target" => $target));
}
public function updateStatusFromServerAction(Request $request, $id)
{
$this->init($request);
/** @var Order $row */
$row = $this->orderRepo->findOneBy(array("id" => $id));
if (!is_object($row)) {
return $this->getJsonResultResponse($request, "Invalid ID");
}
if (!$this->hasUserRole(Role::GLOBAL_ORDER_ADMIN)) {
if ($row->getCustomer()->getId() != $this->getCustomerContext()->getId()) {
return $this->getJsonResultResponse($request, "Permission denied");
}
}
if ($row->getRemoteStatus() != Order::REMOTE_SUCCESS) {
return $this->getJsonResultResponse($request, "Not yet submitted to remote system");
}
if ($row->getOrderId() == "") {
return $this->getJsonResultResponse($request, "Order has no order-no.");
}
return $this->getJsonResultResponse($request, "Tami disabled.");
// $tami = $this->get("diplix.tamiconnector");
// $ok = $tami->getOrderStatus($row);
// if (!$ok) return $this->getJsonResultResponse($request, "Remote operation failed.");
// return $this->getJsonResponse($request, array("success" => true, "status" => $row->getOrderStatus()->getName()));
}
public function changeOrderCustomerAction(Request $request, $id)
{
$this->init($request);
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$this->ensureUserHasRole(Role::DISPO);
/** @var Order $row */
$row = $this->orderRepo->findOneBy(["id" => $id]);
if ($row === null) throw new AccessDeniedHttpException("Invalid ID");
// do not check :: if ($row->getRemoteStatus() != Order::REMOTE_PENDING) throw new AccessDeniedHttpException("Order has already been submitted and cannot be changed anymore.");
$form = $this->createForm(ChangeOrderCustomerForm::class, $row, []);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$this->orderRepo->flush($row);
$this->addFlash('success', 'Änderungen gespeichert.');
}
}
return $this->render("@DiplixKMG/Orders/change-customer.html.twig", [
'form' => $form->createView(),
'row' => $row
]);
}
public function usersForCustomerAction(Request $request, $customerId)
{
$this->init($request);
$customer = $this->managerRegistry
->getRepository(Customer::class)
->findOneBy(array("id" => $customerId));
/** @var UserRepository $uRepo */
$uRepo = $this->managerRegistry->getRepository(User::class);
$list = $uRepo->findUsersOfCustomer($customer);
$resultList = array();
foreach ($list as $user) {
$resultList[$user->getId()] = $user->getLastName().', '.$user->getFirstName();
}
return new JsonResponse([
"users" => $resultList
]);
}
public function pinAction(Request $request, $id)
{
$this->init($request);
$this->denyAccessUnlessGranted(Role::GLOBAL_ORDER_ADMIN);
/** @var Order $row */
$row = $this->orderRepo->findOneBy(array('id' => $id));
if (!is_object($row))
{
throw new NotFoundHttpException('Invalid id');
}
return $this->getJsonDataResponse($request, true, ['pin' => $row->getConfirmationPin()]);
}
public function jobPdfAction(Request $request, $id)
{
$this->init($request);
$this->denyAccessUnlessGranted(Role::GLOBAL_ORDER_ADMIN);
/** @var Order $row */
$row = $this->orderRepo->findOneBy(array('id' => $id));
if (!is_object($row))
{
throw new NotFoundHttpException('Invalid id');
}
$pdfFile = $this->orderHandler->getJobPdf($row,$this->getCurrentUser(),'oc');
return $this->getDownloadResponse($pdfFile,basename($pdfFile));
}
}