Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -9,12 +9,10 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Invoice;
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Entity\MetaTableTypeInterface;
|
||||
use App\Event\InvoiceCreatedMultipleEvent;
|
||||
use App\Event\InvoiceDocumentsEvent;
|
||||
use App\Event\InvoiceMetaDefinitionEvent;
|
||||
use App\Event\InvoiceMetaDisplayEvent;
|
||||
@@ -26,16 +24,21 @@ use App\Form\InvoiceEditForm;
|
||||
use App\Form\InvoiceTemplateForm;
|
||||
use App\Form\Toolbar\InvoiceArchiveForm;
|
||||
use App\Form\Toolbar\InvoiceToolbarForm;
|
||||
use App\Form\Toolbar\InvoiceToolbarSimpleForm;
|
||||
use App\Form\Type\DatePickerType;
|
||||
use App\Form\Type\InvoiceTemplateType;
|
||||
use App\Invoice\ServiceInvoice;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\InvoiceDocumentRepository;
|
||||
use App\Repository\InvoiceRepository;
|
||||
use App\Repository\InvoiceTemplateRepository;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\InvoiceArchiveQuery;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
use App\Utils\DataTable;
|
||||
use App\Utils\PageSetup;
|
||||
use Exception;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -47,30 +50,22 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Controller used to create invoices and manage invoice templates.
|
||||
*
|
||||
* @Route(path="/invoice")
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
#[Route(path: '/invoice')]
|
||||
#[Security("is_granted('IS_AUTHENTICATED_FULLY') and is_granted('view_invoice')")]
|
||||
final class InvoiceController extends AbstractController
|
||||
{
|
||||
private $service;
|
||||
private $templateRepository;
|
||||
private $invoiceRepository;
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $templateRepository, InvoiceRepository $invoiceRepository, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->templateRepository = $templateRepository;
|
||||
$this->invoiceRepository = $invoiceRepository;
|
||||
$this->dispatcher = $dispatcher;
|
||||
public function __construct(
|
||||
private ServiceInvoice $service,
|
||||
private InvoiceTemplateRepository $templateRepository,
|
||||
private InvoiceRepository $invoiceRepository,
|
||||
private EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/", name="invoice", methods={"GET", "POST"})
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
public function indexAction(Request $request, SystemConfiguration $configuration, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
#[Route(path: '/', name: 'invoice', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('create_invoice')")]
|
||||
public function indexAction(Request $request, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
{
|
||||
if (!$this->templateRepository->hasTemplate()) {
|
||||
if ($this->isGranted('manage_invoice_template')) {
|
||||
@@ -81,13 +76,7 @@ final class InvoiceController extends AbstractController
|
||||
|
||||
$query = $this->getDefaultQuery();
|
||||
|
||||
$token = null;
|
||||
if ($request->query->has('token')) {
|
||||
$token = $request->query->get('token');
|
||||
$request->query->remove('token');
|
||||
}
|
||||
|
||||
$form = $this->getToolbarForm($query, $configuration->find('invoice.simple_form'));
|
||||
$form = $this->getToolbarForm($query);
|
||||
if ($this->handleSearch($form, $request)) {
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
@@ -96,53 +85,50 @@ final class InvoiceController extends AbstractController
|
||||
$total = 0;
|
||||
$searched = false;
|
||||
|
||||
if ($form->isValid() && $this->isGranted('create_invoice')) {
|
||||
if ($request->query->has('createInvoice')) {
|
||||
if (!$this->isCsrfTokenValid('invoice.create', $token)) {
|
||||
$this->flashError('action.csrf.error');
|
||||
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
|
||||
$csrfTokenManager->refreshToken('invoice.create');
|
||||
|
||||
try {
|
||||
return $this->renderInvoice($query, $request);
|
||||
} catch (Exception $ex) {
|
||||
$this->logException($ex);
|
||||
$this->flashError('action.update.error', ['%reason%' => 'check doctor/logs']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($form->get('template')->getData() !== null) {
|
||||
try {
|
||||
$models = $this->service->createModels($query);
|
||||
$searched = true;
|
||||
} catch (Exception $ex) {
|
||||
$this->logException($ex);
|
||||
$this->flashError($ex->getMessage());
|
||||
}
|
||||
if ($form->isValid() && $query->getTemplate() !== null) {
|
||||
try {
|
||||
$models = $this->service->createModels($query);
|
||||
$searched = true;
|
||||
} catch (Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
}
|
||||
|
||||
$forms = [];
|
||||
|
||||
foreach ($models as $model) {
|
||||
$customer = $model->getCustomer();
|
||||
$customerTpl = $model->getTemplate();
|
||||
$total += \count($model->getCalculator()->getEntries());
|
||||
|
||||
$values = [
|
||||
'invoiceDate' => $query->getInvoiceDate(),
|
||||
'template' => $customerTpl
|
||||
];
|
||||
|
||||
$forms[] = $this->createFormWithName('customer_' . $customer->getId(), FormType::class, $values, [
|
||||
'csrf_protection' => false,
|
||||
])
|
||||
->add('template', InvoiceTemplateType::class)
|
||||
->add('invoiceDate', DatePickerType::class, [
|
||||
'required' => true,
|
||||
])
|
||||
->createView();
|
||||
}
|
||||
|
||||
return $this->render('invoice/index.html.twig', [
|
||||
'page_setup' => $this->createPageSetup(),
|
||||
'models' => $models,
|
||||
'forms' => $forms,
|
||||
'form' => $form->createView(),
|
||||
'limit_preview' => ($total > 500),
|
||||
'searched' => $searched,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/preview/{customer}/{token}", name="invoice_preview", methods={"GET"})
|
||||
* @Security("is_granted('access', customer)")
|
||||
* @Security("is_granted('create_invoice')")
|
||||
*/
|
||||
public function previewAction(Customer $customer, string $token, Request $request, SystemConfiguration $configuration): Response
|
||||
#[Route(path: '/preview/{customer}/{token}', name: 'invoice_preview', methods: ['GET'])]
|
||||
#[Security("is_granted('access', customer) and is_granted('create_invoice')")]
|
||||
public function previewAction(Customer $customer, string $token, Request $request): Response
|
||||
{
|
||||
if (!$this->templateRepository->hasTemplate()) {
|
||||
return $this->redirectToRoute('invoice');
|
||||
@@ -158,7 +144,8 @@ final class InvoiceController extends AbstractController
|
||||
// so the new token would not be loaded
|
||||
|
||||
$query = $this->getDefaultQuery();
|
||||
$form = $this->getToolbarForm($query, $configuration->find('invoice.simple_form'));
|
||||
$query->setAllowTemplateOverwrite(false);
|
||||
$form = $this->getToolbarForm($query);
|
||||
if ($this->handleSearch($form, $request)) {
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
@@ -168,10 +155,9 @@ final class InvoiceController extends AbstractController
|
||||
$query->setCustomers([$customer]);
|
||||
$model = $this->service->createModel($query);
|
||||
|
||||
return $this->service->renderInvoiceWithModel($model, $this->dispatcher, true);
|
||||
return $this->service->renderInvoice($model, $this->dispatcher, true);
|
||||
} catch (Exception $ex) {
|
||||
$this->logException($ex);
|
||||
$this->flashError('action.update.error', ['%reason%' => $ex->getMessage()]);
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
} else {
|
||||
$this->flashFormError($form);
|
||||
@@ -180,12 +166,9 @@ final class InvoiceController extends AbstractController
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/save-invoice/{customer}/{template}/{token}", name="invoice_create", methods={"GET"})
|
||||
* @Security("is_granted('access', customer)")
|
||||
* @Security("is_granted('create_invoice')")
|
||||
*/
|
||||
public function createInvoiceAction(Customer $customer, InvoiceTemplate $template, string $token, Request $request, SystemConfiguration $configuration, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
#[Route(path: '/save-invoice/{customer}/{token}', name: 'invoice_create', methods: ['GET'])]
|
||||
#[Security("is_granted('access', customer) and is_granted('create_invoice')")]
|
||||
public function createInvoiceAction(Customer $customer, string $token, Request $request, CustomerRepository $customerRepository): Response
|
||||
{
|
||||
if (!$this->templateRepository->hasTemplate()) {
|
||||
return $this->redirectToRoute('invoice');
|
||||
@@ -198,28 +181,40 @@ final class InvoiceController extends AbstractController
|
||||
}
|
||||
|
||||
$query = $this->getDefaultQuery();
|
||||
$form = $this->getToolbarForm($query, $configuration->find('invoice.simple_form'));
|
||||
$query->setAllowTemplateOverwrite(false);
|
||||
$form = $this->getToolbarForm($query);
|
||||
if ($this->handleSearch($form, $request)) {
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
|
||||
if ($form->isValid()) {
|
||||
$query->setTemplate($template);
|
||||
$query->setCustomers([$customer]);
|
||||
try {
|
||||
$query->setCustomers([$customer]);
|
||||
$model = $this->service->createModel($query);
|
||||
|
||||
return $this->renderInvoice($query, $request);
|
||||
// save default template for customer if not yet set
|
||||
if ($customer->getInvoiceTemplate() === null) {
|
||||
$customer->setInvoiceTemplate($query->getTemplate());
|
||||
$customerRepository->saveCustomer($customer);
|
||||
}
|
||||
|
||||
$invoice = $this->service->createInvoice($model, $this->dispatcher);
|
||||
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_list', ['id' => $invoice->getId()]);
|
||||
} catch (Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
} else {
|
||||
$this->flashFormError($form);
|
||||
}
|
||||
|
||||
$this->flashFormError($form);
|
||||
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/change-status/{id}/{status}/{token}", name="admin_invoice_status", methods={"GET", "POST"})
|
||||
* @Security("is_granted('access', invoice.getCustomer())")
|
||||
* @Security("is_granted('create_invoice')")
|
||||
*/
|
||||
#[Route(path: '/change-status/{id}/{status}/{token}', name: 'admin_invoice_status', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('create_invoice')")]
|
||||
public function changeStatusAction(Invoice $invoice, string $status, string $token, Request $request, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
{
|
||||
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.status', $token))) {
|
||||
@@ -238,6 +233,7 @@ final class InvoiceController extends AbstractController
|
||||
$form->handleRequest($request);
|
||||
|
||||
return $this->render('invoice/invoice_edit.html.twig', [
|
||||
'page_setup' => $this->createPageSetup(),
|
||||
'invoice' => $invoice,
|
||||
'form' => $form->createView()
|
||||
]);
|
||||
@@ -253,11 +249,8 @@ final class InvoiceController extends AbstractController
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/edit/{id}", name="admin_invoice_edit", methods={"GET", "POST"})
|
||||
* @Security("is_granted('access', invoice.getCustomer())")
|
||||
* @Security("is_granted('create_invoice')")
|
||||
*/
|
||||
#[Route(path: '/edit/{id}', name: 'admin_invoice_edit', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('create_invoice')")]
|
||||
public function editAction(Invoice $invoice, Request $request): Response
|
||||
{
|
||||
$form = $this->createInvoiceEditForm($invoice);
|
||||
@@ -267,24 +260,22 @@ final class InvoiceController extends AbstractController
|
||||
try {
|
||||
$this->invoiceRepository->saveInvoice($invoice);
|
||||
$this->flashSuccess('action.update.success');
|
||||
} catch (Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
} catch (Exception $ex) {
|
||||
$this->handleFormUpdateException($ex, $form);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('invoice/invoice_edit.html.twig', [
|
||||
'page_setup' => $this->createPageSetup(),
|
||||
'invoice' => $invoice,
|
||||
'form' => $form->createView()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/delete/{id}/{token}", name="admin_invoice_delete", methods={"GET"})
|
||||
* @Security("is_granted('access', invoice.getCustomer())")
|
||||
* @Security("is_granted('delete_invoice')")
|
||||
*/
|
||||
#[Route(path: '/delete/{id}/{token}', name: 'admin_invoice_delete', methods: ['GET'])]
|
||||
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('delete_invoice')")]
|
||||
public function deleteInvoiceAction(Invoice $invoice, string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
{
|
||||
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.status', $token))) {
|
||||
@@ -305,11 +296,8 @@ final class InvoiceController extends AbstractController
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/download/{id}", name="admin_invoice_download", methods={"GET"})
|
||||
* @Security("is_granted('access', invoice.getCustomer())")
|
||||
* @Security("is_granted('create_invoice')")
|
||||
*/
|
||||
#[Route(path: '/download/{id}', name: 'admin_invoice_download', methods: ['GET'])]
|
||||
#[Security("is_granted('access', invoice.getCustomer()) and is_granted('view_invoice')")]
|
||||
public function downloadAction(Invoice $invoice): Response
|
||||
{
|
||||
$file = $this->service->getInvoiceFile($invoice);
|
||||
@@ -323,10 +311,8 @@ final class InvoiceController extends AbstractController
|
||||
return $this->file($file->getRealPath(), $file->getBasename());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/show/{page}", defaults={"page": 1}, requirements={"page": "[1-9]\d*"}, name="admin_invoice_list", methods={"GET"})
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
#[Route(path: '/show/{page}', defaults: ['page' => 1], requirements: ['page' => '[1-9]\d*'], name: 'admin_invoice_list', methods: ['GET'])]
|
||||
#[Security("is_granted('view_invoice')")]
|
||||
public function showInvoicesAction(Request $request, int $page): Response
|
||||
{
|
||||
$invoice = null;
|
||||
@@ -344,21 +330,48 @@ final class InvoiceController extends AbstractController
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepository->getPagerfantaForQuery($query);
|
||||
$entries = $this->invoiceRepository->getPagerfantaForQuery($query);
|
||||
$metaColumns = $this->findMetaColumns($query);
|
||||
|
||||
$table = new DataTable('invoices', $query);
|
||||
$table->setPagination($entries);
|
||||
$table->setSearchForm($form);
|
||||
$table->setPaginationRoute('admin_invoice_list');
|
||||
$table->setReloadEvents('kimai.invoiceUpdate');
|
||||
|
||||
$table->addColumn('avatar', ['class' => 'text-nowrap w-avatar d-none d-md-table-cell', 'title' => false, 'orderBy' => false]);
|
||||
$table->addColumn('date', ['class' => 'd-none d-sm-table-cell text-nowrap w-min']);
|
||||
$table->addColumn('user', ['class' => 'd-none text-nowrap w-min', 'orderBy' => false]);
|
||||
$table->addColumn('customer', ['class' => 'alwaysVisible text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('comment', ['class' => 'd-none', 'title' => 'description']);
|
||||
|
||||
foreach ($metaColumns as $metaColumn) {
|
||||
$table->addColumn('mf_' . $metaColumn->getName(), ['title' => $metaColumn->getLabel(), 'class' => 'd-none', 'orderBy' => false]);
|
||||
}
|
||||
|
||||
$table->addColumn('invoice_number', ['class' => 'd-none d-md-table-cell w-min', 'title' => 'invoice.number', 'orderBy' => false]);
|
||||
$table->addColumn('due_date', ['class' => 'd-none w-min', 'title' => 'invoice.due_days', 'orderBy' => false]);
|
||||
$table->addColumn('payment_date', ['class' => 'd-none w-min', 'title' => 'invoice.payment_date', 'orderBy' => false]);
|
||||
$table->addColumn('status', ['class' => 'd-none d-sm-table-cell w-min', 'orderBy' => false]);
|
||||
$table->addColumn('subtotal', ['class' => 'd-none text-end w-min', 'title' => 'invoice.subtotal', 'orderBy' => false]);
|
||||
$table->addColumn('tax', ['class' => 'd-none text-end w-min', 'title' => 'invoice.tax']);
|
||||
$table->addColumn('total_rate', ['class' => 'd-none d-md-table-cell text-end w-min']);
|
||||
$table->addColumn('actions', ['class' => 'actions']);
|
||||
|
||||
$page = $this->createPageSetup('all_invoices');
|
||||
$page->setDataTable($table);
|
||||
$page->setActionName('invoice_archive');
|
||||
|
||||
return $this->render('invoice/listing.html.twig', [
|
||||
'entries' => $invoices,
|
||||
'query' => $query,
|
||||
'toolbarForm' => $form->createView(),
|
||||
'page_setup' => $page,
|
||||
'dataTable' => $table,
|
||||
'download' => $invoice,
|
||||
'metaColumns' => $this->findMetaColumns($query),
|
||||
'metaColumns' => $metaColumns,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/export", name="invoice_export", methods={"GET"})
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
#[Route(path: '/export', name: 'invoice_export', methods: ['GET'])]
|
||||
#[Security("is_granted('view_invoice')")]
|
||||
public function exportAction(Request $request, EntityWithMetaFieldsExporter $exporter)
|
||||
{
|
||||
$query = new InvoiceArchiveQuery();
|
||||
@@ -380,35 +393,52 @@ final class InvoiceController extends AbstractController
|
||||
return $writer->getFileResponse($spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/template/{page}", requirements={"page": "[1-9]\d*"}, defaults={"page": 1}, name="admin_invoice_template", methods={"GET", "POST"})
|
||||
* @Security("is_granted('manage_invoice_template')")
|
||||
*/
|
||||
#[Route(path: '/template/{page}', requirements: ['page' => '[1-9]\d*'], defaults: ['page' => 1], name: 'admin_invoice_template', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function listTemplateAction(int $page): Response
|
||||
{
|
||||
$query = new BaseQuery();
|
||||
$query->setPage($page);
|
||||
|
||||
$templates = $this->templateRepository->getPagerfantaForQuery($query);
|
||||
$entries = $this->templateRepository->getPagerfantaForQuery($query);
|
||||
|
||||
$table = new DataTable('invoice_template', $query);
|
||||
$table->setPagination($entries);
|
||||
$table->setPaginationRoute('admin_invoice_template');
|
||||
$table->setReloadEvents('kimai.invoiceTemplateUpdate');
|
||||
|
||||
$table->addColumn('name', ['class' => 'alwaysVisible', 'orderBy' => false]);
|
||||
$table->addColumn('title', ['class' => 'd-none text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('company', ['class' => 'd-none', 'orderBy' => false]);
|
||||
$table->addColumn('vat_id', ['class' => 'd-none text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('tax_rate', ['class' => 'd-none text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('due_days', ['class' => 'd-none text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('address', ['class' => 'd-none', 'orderBy' => false]);
|
||||
$table->addColumn('contact', ['class' => 'd-none', 'orderBy' => false]);
|
||||
$table->addColumn('calculator', ['class' => 'd-none', 'orderBy' => false, 'title' => 'invoice_calculator', 'translation_domain' => 'invoice-calculator']);
|
||||
$table->addColumn('renderer', ['class' => 'd-none', 'orderBy' => false, 'title' => 'invoice_renderer', 'translation_domain' => 'invoice-renderer']);
|
||||
$table->addColumn('language', ['class' => 'd-none text-nowrap', 'orderBy' => false]);
|
||||
$table->addColumn('actions', ['class' => 'actions', 'orderBy' => false]);
|
||||
|
||||
$page = $this->createPageSetup('admin_invoice_template.title');
|
||||
$page->setDataTable($table);
|
||||
$page->setActionName('invoice_templates');
|
||||
|
||||
return $this->render('invoice/templates.html.twig', [
|
||||
'entries' => $templates,
|
||||
'page_setup' => $page,
|
||||
'dataTable' => $table,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/template/{id}/edit", name="admin_invoice_template_edit", methods={"GET", "POST"})
|
||||
* @Security("is_granted('manage_invoice_template')")
|
||||
*/
|
||||
#[Route(path: '/template/{id}/edit', name: 'admin_invoice_template_edit', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function editTemplateAction(InvoiceTemplate $template, Request $request): Response
|
||||
{
|
||||
return $this->renderTemplateForm($template, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/document_upload", name="admin_invoice_document_upload", methods={"GET", "POST"})
|
||||
* @Security("is_granted('upload_invoice_template')")
|
||||
*/
|
||||
#[Route(path: '/document_upload', name: 'admin_invoice_document_upload', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('upload_invoice_template')")]
|
||||
public function uploadDocumentAction(Request $request, string $projectDirectory, InvoiceDocumentRepository $documentRepository)
|
||||
{
|
||||
$dir = $documentRepository->getUploadDirectory();
|
||||
@@ -450,7 +480,7 @@ final class InvoiceController extends AbstractController
|
||||
}
|
||||
|
||||
if (!file_exists($invoiceDir)) {
|
||||
@mkdir($invoiceDir, 0777);
|
||||
@mkdir($invoiceDir, 0o777);
|
||||
}
|
||||
|
||||
if (!is_dir($invoiceDir)) {
|
||||
@@ -494,7 +524,10 @@ final class InvoiceController extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
$page = $this->createPageSetup('admin_invoice_template.title');
|
||||
|
||||
return $this->render('invoice/document_upload.html.twig', [
|
||||
'page_setup' => $page,
|
||||
'error_replacer' => ['%max%' => $event->getMaximumAllowedDocuments(), '%dir%' => $dir],
|
||||
'upload_error' => $uploadError,
|
||||
'can_upload' => $canUpload,
|
||||
@@ -504,10 +537,8 @@ final class InvoiceController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/document/{id}/delete/{token}", name="invoice_document_delete", methods={"GET", "POST"})
|
||||
* @Security("is_granted('manage_invoice_template')")
|
||||
*/
|
||||
#[Route(path: '/document/{id}/delete/{token}', name: 'invoice_document_delete', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function deleteDocument(string $id, string $token, CsrfTokenManagerInterface $csrfTokenManager, InvoiceDocumentRepository $documentRepository): Response
|
||||
{
|
||||
$document = $documentRepository->findByName($id);
|
||||
@@ -549,30 +580,38 @@ final class InvoiceController extends AbstractController
|
||||
return $this->redirectToRoute('admin_invoice_document_upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/template/create", name="admin_invoice_template_create", methods={"GET", "POST"})
|
||||
* @Route(path="/template/create/{id}", name="admin_invoice_template_copy", methods={"GET", "POST"})
|
||||
* @Security("is_granted('manage_invoice_template')")
|
||||
*/
|
||||
public function createTemplateAction(Request $request, ?InvoiceTemplate $copyFrom): Response
|
||||
#[Route(path: '/template/create/{id}', name: 'admin_invoice_template_copy', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function copyTemplateAction(Request $request, InvoiceTemplate $copyFrom): Response
|
||||
{
|
||||
return $this->createTemplate($request, $copyFrom);
|
||||
}
|
||||
|
||||
#[Route(path: '/template/create', name: 'admin_invoice_template_create', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function createTemplateAction(Request $request): Response
|
||||
{
|
||||
return $this->createTemplate($request, null);
|
||||
}
|
||||
|
||||
private function createTemplate(Request $request, ?InvoiceTemplate $copyFrom = null): Response
|
||||
{
|
||||
$template = new InvoiceTemplate();
|
||||
$template->setLanguage($request->getLocale());
|
||||
|
||||
if (null !== $copyFrom) {
|
||||
$template = clone $copyFrom;
|
||||
$template->setName('Copy of ' . $copyFrom->getName());
|
||||
$template->setName($copyFrom->getName() . ' (1)');
|
||||
}
|
||||
|
||||
return $this->renderTemplateForm($template, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/template/{id}/delete/{token}", name="admin_invoice_template_delete", methods={"GET", "POST"})
|
||||
* @Security("is_granted('manage_invoice_template')")
|
||||
*/
|
||||
public function deleteTemplate(InvoiceTemplate $template, string $token, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
#[Route(path: '/template/{id}/delete/{csrfToken}', name: 'admin_invoice_template_delete', methods: ['GET', 'POST'])]
|
||||
#[Security("is_granted('manage_invoice_template')")]
|
||||
public function deleteTemplate(InvoiceTemplate $template, string $csrfToken, CsrfTokenManagerInterface $csrfTokenManager): Response
|
||||
{
|
||||
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.delete_template', $token))) {
|
||||
if (!$csrfTokenManager->isTokenValid(new CsrfToken('invoice.delete_template', $csrfToken))) {
|
||||
$this->flashError('action.csrf.error');
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_template');
|
||||
@@ -599,6 +638,7 @@ final class InvoiceController extends AbstractController
|
||||
$query = new InvoiceQuery();
|
||||
$query->setBegin($begin);
|
||||
$query->setEnd($end);
|
||||
$query->setInvoiceDate($factory->createDateTime());
|
||||
// limit access to data from teams
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
@@ -610,32 +650,6 @@ final class InvoiceController extends AbstractController
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function renderInvoice(InvoiceQuery $query, Request $request)
|
||||
{
|
||||
// use the current request locale as fallback, if no translation was configured
|
||||
if (null !== $query->getTemplate() && null === $query->getTemplate()->getLanguage()) {
|
||||
$query->getTemplate()->setLanguage($request->getLocale());
|
||||
}
|
||||
|
||||
try {
|
||||
$invoices = $this->service->createInvoices($query, $this->dispatcher);
|
||||
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
if (\count($invoices) === 1) {
|
||||
return $this->redirectToRoute('admin_invoice_list', ['id' => $invoices[0]->getId()]);
|
||||
} elseif (\count($invoices) > 1) {
|
||||
$this->dispatcher->dispatch(new InvoiceCreatedMultipleEvent($invoices));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
} catch (Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('invoice');
|
||||
}
|
||||
|
||||
private function flashFormError(FormInterface $form): void
|
||||
{
|
||||
$err = '';
|
||||
@@ -643,7 +657,7 @@ final class InvoiceController extends AbstractController
|
||||
$err .= PHP_EOL . '[' . $error->getOrigin()->getName() . '] ' . $error->getMessage();
|
||||
}
|
||||
|
||||
$this->flashError('action.update.error', ['%reason%' => $err]);
|
||||
$this->flashError('action.update.error', $err);
|
||||
}
|
||||
|
||||
private function renderTemplateForm(InvoiceTemplate $template, Request $request): Response
|
||||
@@ -659,25 +673,24 @@ final class InvoiceController extends AbstractController
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_template');
|
||||
} catch (Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
$this->handleFormUpdateException($ex, $editForm);
|
||||
}
|
||||
}
|
||||
|
||||
$page = $this->createPageSetup('admin_invoice_template.title');
|
||||
|
||||
return $this->render('invoice/template_edit.html.twig', [
|
||||
'page_setup' => $page,
|
||||
'template' => $template,
|
||||
'form' => $editForm->createView()
|
||||
]);
|
||||
}
|
||||
|
||||
private function getToolbarForm(InvoiceQuery $query, bool $simple): FormInterface
|
||||
private function getToolbarForm(InvoiceQuery $query): FormInterface
|
||||
{
|
||||
$form = $simple ? InvoiceToolbarSimpleForm::class : InvoiceToolbarForm::class;
|
||||
|
||||
return $this->createForm($form, $query, [
|
||||
return $this->createSearchForm(InvoiceToolbarForm::class, $query, [
|
||||
'action' => $this->generateUrl('invoice', []),
|
||||
'method' => 'GET',
|
||||
'include_user' => $this->isGranted('view_other_timesheet'),
|
||||
'include_export' => $this->isGranted('edit_export_other_timesheet'),
|
||||
'timezone' => $this->getDateTimeFactory()->getTimezone()->getName(),
|
||||
'attr' => [
|
||||
'id' => 'invoice-print-form'
|
||||
@@ -687,9 +700,8 @@ final class InvoiceController extends AbstractController
|
||||
|
||||
private function getArchiveToolbarForm(InvoiceArchiveQuery $query): FormInterface
|
||||
{
|
||||
return $this->createForm(InvoiceArchiveForm::class, $query, [
|
||||
return $this->createSearchForm(InvoiceArchiveForm::class, $query, [
|
||||
'action' => $this->generateUrl('admin_invoice_list', []),
|
||||
'method' => 'GET',
|
||||
'timezone' => $this->getDateTimeFactory()->getTimezone()->getName(),
|
||||
'attr' => [
|
||||
'id' => 'invoice-archive-form'
|
||||
@@ -734,4 +746,12 @@ final class InvoiceController extends AbstractController
|
||||
'timezone' => $this->getDateTimeFactory()->getTimezone()->getName(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createPageSetup(string $title = 'invoices'): PageSetup
|
||||
{
|
||||
$page = new PageSetup($title);
|
||||
$page->setHelp('invoices.html');
|
||||
|
||||
return $page;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user