added invoice archive & configurable invoice numbers (#1541)
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractController;
|
||||
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||
use Symfony\Contracts\Service\ServiceSubscriberInterface;
|
||||
@@ -20,13 +21,6 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
*/
|
||||
abstract class AbstractController extends BaseAbstractController implements ServiceSubscriberInterface
|
||||
{
|
||||
public const FLASH_SUCCESS = 'success';
|
||||
public const FLASH_WARNING = 'warning';
|
||||
public const FLASH_ERROR = 'error';
|
||||
|
||||
public const DOMAIN_FLASH = 'flashmessages';
|
||||
public const DOMAIN_ERROR = 'exceptions';
|
||||
|
||||
/**
|
||||
* @deprecated since 1.6, will be removed with 2.0
|
||||
*/
|
||||
@@ -40,6 +34,14 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
return $this->container->get('translator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoggerInterface $logger
|
||||
*/
|
||||
private function getLogger()
|
||||
{
|
||||
return $this->container->get('logger');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User|null
|
||||
*/
|
||||
@@ -56,7 +58,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
*/
|
||||
protected function flashSuccess($translationKey, $parameter = [])
|
||||
{
|
||||
$this->addFlashTranslated(self::FLASH_SUCCESS, $translationKey, $parameter);
|
||||
$this->addFlashTranslated('success', $translationKey, $parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +69,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
*/
|
||||
protected function flashWarning($translationKey, $parameter = [])
|
||||
{
|
||||
$this->addFlashTranslated(self::FLASH_WARNING, $translationKey, $parameter);
|
||||
$this->addFlashTranslated('warning', $translationKey, $parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +80,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
*/
|
||||
protected function flashError($translationKey, $parameter = [])
|
||||
{
|
||||
$this->addFlashTranslated(self::FLASH_ERROR, $translationKey, $parameter);
|
||||
$this->addFlashTranslated('error', $translationKey, $parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,22 +94,28 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
{
|
||||
if (!empty($parameter)) {
|
||||
foreach ($parameter as $key => $value) {
|
||||
$parameter[$key] = $this->getTranslator()->trans($value, [], self::DOMAIN_FLASH);
|
||||
$parameter[$key] = $this->getTranslator()->trans($value, [], 'flashmessages');
|
||||
}
|
||||
$message = $this->getTranslator()->trans(
|
||||
$message,
|
||||
$parameter,
|
||||
self::DOMAIN_FLASH
|
||||
'flashmessages'
|
||||
);
|
||||
}
|
||||
|
||||
$this->addFlash($type, $message);
|
||||
}
|
||||
|
||||
protected function logException(\Exception $ex)
|
||||
{
|
||||
$this->getLogger()->critical($ex->getMessage());
|
||||
}
|
||||
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return array_merge(parent::getSubscribedServices(), [
|
||||
'translator' => TranslatorInterface::class
|
||||
'translator' => TranslatorInterface::class,
|
||||
'logger' => LoggerInterface::class
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,21 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Invoice;
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Event\InvoicePostRenderEvent;
|
||||
use App\Event\InvoicePreRenderEvent;
|
||||
use App\Export\ExportItemInterface;
|
||||
use App\Form\InvoiceDocumentUploadForm;
|
||||
use App\Form\InvoiceTemplateForm;
|
||||
use App\Form\Toolbar\InvoiceToolbarForm;
|
||||
use App\Form\Toolbar\InvoiceToolbarSimpleForm;
|
||||
use App\Invoice\InvoiceFormatter;
|
||||
use App\Invoice\InvoiceItemInterface;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\ServiceInvoice;
|
||||
use App\Repository\InvoiceDocumentRepository;
|
||||
use App\Repository\InvoiceRepository;
|
||||
use App\Repository\InvoiceTemplateRepository;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
@@ -48,7 +52,7 @@ final class InvoiceController extends AbstractController
|
||||
/**
|
||||
* @var InvoiceTemplateRepository
|
||||
*/
|
||||
private $invoiceRepository;
|
||||
private $templateRepository;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
@@ -61,11 +65,16 @@ final class InvoiceController extends AbstractController
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
/**
|
||||
* @var InvoiceRepository
|
||||
*/
|
||||
private $invoiceRepository;
|
||||
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, UserDateTimeFactory $dateTimeFactory, InvoiceFormatter $formatter, EventDispatcherInterface $dispatcher)
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $templateRepository, InvoiceRepository $invoiceRepository, UserDateTimeFactory $dateTimeFactory, InvoiceFormatter $formatter, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->invoiceRepository = $invoice;
|
||||
$this->templateRepository = $templateRepository;
|
||||
$this->invoiceRepository = $invoiceRepository;
|
||||
$this->dateTimeFactory = $dateTimeFactory;
|
||||
$this->formatter = $formatter;
|
||||
$this->dispatcher = $dispatcher;
|
||||
@@ -75,9 +84,9 @@ final class InvoiceController extends AbstractController
|
||||
* @Route(path="/", name="invoice", methods={"GET", "POST"})
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
public function indexAction(Request $request): Response
|
||||
public function indexAction(Request $request, SystemConfiguration $configuration): Response
|
||||
{
|
||||
if (!$this->invoiceRepository->hasTemplate()) {
|
||||
if (!$this->templateRepository->hasTemplate()) {
|
||||
if ($this->isGranted('manage_invoice_template')) {
|
||||
return $this->redirectToRoute('admin_invoice_template_create');
|
||||
}
|
||||
@@ -88,16 +97,27 @@ final class InvoiceController extends AbstractController
|
||||
$entries = [];
|
||||
|
||||
$query = $this->getDefaultQuery();
|
||||
$form = $this->getToolbarForm($query, 'GET');
|
||||
$form = $this->getToolbarForm($query, $configuration->find('invoice.simple_form'));
|
||||
$form->setData($query);
|
||||
$form->submit($request->query->all(), false);
|
||||
|
||||
if ($this->isGranted('create_invoice')) {
|
||||
if ($form->isValid()) {
|
||||
/** @var SubmitButton $createButton */
|
||||
$createButton = $form->get('create');
|
||||
if ($createButton->isClicked()) {
|
||||
return $this->renderInvoice($query);
|
||||
try {
|
||||
/** @var SubmitButton $createButton */
|
||||
$createButton = $form->get('create');
|
||||
if ($createButton->isClicked()) {
|
||||
return $this->renderInvoice($query, true);
|
||||
}
|
||||
|
||||
/** @var SubmitButton $printButton */
|
||||
$printButton = $form->get('print');
|
||||
if ($printButton->isClicked()) {
|
||||
return $this->renderInvoice($query, false);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->logException($ex);
|
||||
$this->flashError('action.update.error', ['%reason%' => 'check doctor/logs']);
|
||||
}
|
||||
|
||||
/** @var SubmitButton $previewButton */
|
||||
@@ -144,7 +164,7 @@ final class InvoiceController extends AbstractController
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function renderInvoice(InvoiceQuery $query)
|
||||
protected function renderInvoice(InvoiceQuery $query, bool $saveInvoice = false)
|
||||
{
|
||||
$entries = $this->getEntries($query);
|
||||
$model = $this->prepareModel($query);
|
||||
@@ -162,11 +182,28 @@ final class InvoiceController extends AbstractController
|
||||
$this->dispatcher->dispatch(new InvoicePreRenderEvent($model, $document, $renderer));
|
||||
|
||||
$response = $renderer->render($document, $model);
|
||||
if ($query->isMarkAsExported()) {
|
||||
$this->markEntriesAsExported($entries);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(new InvoicePostRenderEvent($model, $document, $renderer, $response));
|
||||
if ($saveInvoice) {
|
||||
if ($query->isMarkAsExported()) {
|
||||
$this->markEntriesAsExported($entries);
|
||||
}
|
||||
|
||||
$event = new InvoicePostRenderEvent($model, $document, $renderer, $response);
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
$invoiceFilename = $this->service->saveGeneratedInvoice($event);
|
||||
|
||||
$invoice = new Invoice();
|
||||
$invoice->setModel($model);
|
||||
$invoice->setFilename($invoiceFilename);
|
||||
$this->invoiceRepository->saveInvoice($invoice);
|
||||
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
if ($this->isGranted('history_invoice')) {
|
||||
return $this->redirectToRoute('admin_invoice_list', ['id' => $invoice->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
@@ -180,7 +217,81 @@ final class InvoiceController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InvoiceItemInterface[] $entries
|
||||
* @Route(path="/change-status/{id}/{status}", name="admin_invoice_status", methods={"GET"})
|
||||
* @Security("is_granted('history_invoice')")
|
||||
*/
|
||||
public function changeStatusAction(Invoice $invoice, string $status): Response
|
||||
{
|
||||
if (!in_array($status, [Invoice::STATUS_NEW, Invoice::STATUS_PENDING, Invoice::STATUS_PAID])) {
|
||||
throw $this->createNotFoundException('Unknwon invoice status');
|
||||
}
|
||||
|
||||
switch ($status) {
|
||||
case Invoice::STATUS_NEW:
|
||||
$invoice->setIsNew();
|
||||
break;
|
||||
|
||||
case Invoice::STATUS_PENDING:
|
||||
$invoice->setIsPending();
|
||||
break;
|
||||
|
||||
case Invoice::STATUS_PAID:
|
||||
$invoice->setIsPaid();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->invoiceRepository->saveInvoice($invoice);
|
||||
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/download/{id}", name="admin_invoice_download", methods={"GET"})
|
||||
* @Security("is_granted('history_invoice')")
|
||||
*/
|
||||
public function downloadAction(Invoice $invoice): Response
|
||||
{
|
||||
$file = $this->service->getInvoiceFile($invoice);
|
||||
|
||||
if (null === $file) {
|
||||
throw $this->createNotFoundException(
|
||||
sprintf('Invoice file "%s" could not be found for invoice ID "%s"', $invoice->getInvoiceFilename(), $invoice->getId())
|
||||
);
|
||||
}
|
||||
|
||||
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('history_invoice')")
|
||||
*/
|
||||
public function showInvoicesAction(Request $request, int $page): Response
|
||||
{
|
||||
$invoice = null;
|
||||
|
||||
if (null !== ($id = $request->get('id'))) {
|
||||
$invoice = $this->invoiceRepository->find($id);
|
||||
}
|
||||
|
||||
$query = new InvoiceQuery();
|
||||
$query->setOrderBy('date');
|
||||
$query->setPage($page);
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
$invoices = $this->invoiceRepository->getPagerfantaForQuery($query);
|
||||
|
||||
return $this->render('invoice/listing.html.twig', [
|
||||
'entries' => $invoices,
|
||||
'query' => $query,
|
||||
'download' => $invoice,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExportItemInterface[] $entries
|
||||
*/
|
||||
private function markEntriesAsExported(iterable $entries)
|
||||
{
|
||||
@@ -197,7 +308,7 @@ final class InvoiceController extends AbstractController
|
||||
|
||||
/**
|
||||
* @param InvoiceQuery $query
|
||||
* @return InvoiceItemInterface[]
|
||||
* @return ExportItemInterface[]
|
||||
*/
|
||||
protected function getEntries(InvoiceQuery $query): array
|
||||
{
|
||||
@@ -246,6 +357,7 @@ final class InvoiceController extends AbstractController
|
||||
{
|
||||
$model = new InvoiceModel($this->formatter);
|
||||
$model
|
||||
->setInvoiceDate($this->dateTimeFactory->createDateTime())
|
||||
->setQuery($query)
|
||||
->setUser($this->getUser())
|
||||
->setCustomer($query->getCustomer())
|
||||
@@ -276,7 +388,7 @@ final class InvoiceController extends AbstractController
|
||||
*/
|
||||
public function listTemplateAction(): Response
|
||||
{
|
||||
$templates = $this->invoiceRepository->getPagerfantaForQuery(new BaseQuery());
|
||||
$templates = $this->templateRepository->getPagerfantaForQuery(new BaseQuery());
|
||||
|
||||
return $this->render('invoice/templates.html.twig', [
|
||||
'entries' => $templates,
|
||||
@@ -361,7 +473,7 @@ final class InvoiceController extends AbstractController
|
||||
*/
|
||||
public function createTemplateAction(Request $request, ?InvoiceTemplate $copyFrom): Response
|
||||
{
|
||||
if (!$this->invoiceRepository->hasTemplate()) {
|
||||
if (!$this->templateRepository->hasTemplate()) {
|
||||
$this->flashWarning('invoice.first_template');
|
||||
}
|
||||
|
||||
@@ -394,7 +506,7 @@ final class InvoiceController extends AbstractController
|
||||
public function deleteTemplate(InvoiceTemplate $template, Request $request): Response
|
||||
{
|
||||
try {
|
||||
$this->invoiceRepository->removeTemplate($template);
|
||||
$this->templateRepository->removeTemplate($template);
|
||||
$this->flashSuccess('action.delete.success');
|
||||
} catch (\Exception $ex) {
|
||||
$this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]);
|
||||
@@ -411,7 +523,7 @@ final class InvoiceController extends AbstractController
|
||||
|
||||
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||
try {
|
||||
$this->invoiceRepository->saveTemplate($template);
|
||||
$this->templateRepository->saveTemplate($template);
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_invoice_template');
|
||||
@@ -426,11 +538,13 @@ final class InvoiceController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getToolbarForm(InvoiceQuery $query, string $method): FormInterface
|
||||
protected function getToolbarForm(InvoiceQuery $query, bool $simple): FormInterface
|
||||
{
|
||||
return $this->createForm(InvoiceToolbarForm::class, $query, [
|
||||
$form = $simple ? InvoiceToolbarSimpleForm::class : InvoiceToolbarForm::class;
|
||||
|
||||
return $this->createForm($form, $query, [
|
||||
'action' => $this->generateUrl('invoice', []),
|
||||
'method' => $method,
|
||||
'method' => 'GET',
|
||||
'include_user' => $this->isGranted('view_other_timesheet'),
|
||||
'attr' => [
|
||||
'id' => 'invoice-print-form'
|
||||
|
||||
@@ -20,6 +20,7 @@ use App\Form\Type\RoundingModeType;
|
||||
use App\Form\Type\SkinType;
|
||||
use App\Form\Type\TrackingModeType;
|
||||
use App\Form\Type\WeekDaysType;
|
||||
use App\Form\Type\YesNoType;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use App\Validator\Constraints\DateTimeFormat;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
@@ -275,6 +276,22 @@ final class SystemConfigurationController extends AbstractController
|
||||
->setType(WeekDaysType::class)
|
||||
->setTranslationDomain('system-configuration'),
|
||||
]),
|
||||
(new SystemConfigurationModel())
|
||||
->setSection(SystemConfigurationModel::SECTION_FORM_INVOICE)
|
||||
->setConfiguration([
|
||||
(new Configuration())
|
||||
->setName('invoice.number_format')
|
||||
->setLabel('invoice.number_format')
|
||||
->setRequired(true)
|
||||
->setType(TextType::class) // TODO that should be a custom type with validation
|
||||
->setTranslationDomain('system-configuration'),
|
||||
(new Configuration())
|
||||
->setName('invoice.simple_form')
|
||||
->setLabel('simple_form')
|
||||
->setRequired(false)
|
||||
->setType(YesNoType::class)
|
||||
->setTranslationDomain('system-configuration'),
|
||||
]),
|
||||
(new SystemConfigurationModel())
|
||||
->setSection(SystemConfigurationModel::SECTION_FORM_CUSTOMER)
|
||||
->setConfiguration([
|
||||
|
||||
Reference in New Issue
Block a user