This repository has been archived by the owner on Jun 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
57310c7
commit fabbcf4
Showing
13 changed files
with
547 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DoctrineMigrations; | ||
|
||
use Doctrine\DBAL\Schema\Schema; | ||
use Doctrine\Migrations\AbstractMigration; | ||
|
||
/** | ||
* Auto-generated Migration: Please modify to your needs! | ||
*/ | ||
final class Version20230527111225 extends AbstractMigration | ||
{ | ||
public function getDescription(): string | ||
{ | ||
return ''; | ||
} | ||
|
||
public function up(Schema $schema): void | ||
{ | ||
// this up() migration is auto-generated, please modify it to your needs | ||
$this->addSql('CREATE TABLE vat_calculation (id INT AUTO_INCREMENT NOT NULL, v DOUBLE PRECISION NOT NULL, r DOUBLE PRECISION NOT NULL, is_vat_included TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | ||
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); | ||
} | ||
|
||
public function down(Schema $schema): void | ||
{ | ||
// this down() migration is auto-generated, please modify it to your needs | ||
$this->addSql('DROP TABLE vat_calculation'); | ||
$this->addSql('DROP TABLE messenger_messages'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
vat-calculations.csv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
/* | ||
What this does | ||
This controller handles the `/delete-calculations` route. | ||
It fetches all calculations from the database using the findAll() method. | ||
We then loop over each calculation and delete it. | ||
We then redirect to the homepage to show that there are no more calculations. | ||
*/ | ||
|
||
namespace App\Controller; | ||
|
||
use App\Entity\VatCalculation; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
class DeleteCalculationsController extends AbstractController | ||
{ | ||
public function new(EntityManagerInterface $entityManager): Response | ||
{ | ||
$calculations = $entityManager->getRepository(VatCalculation::class)->findAll(); | ||
|
||
foreach ($calculations as $calculation) { | ||
$entityManager->remove($calculation); | ||
} | ||
$entityManager->flush(); | ||
|
||
return $this->redirectToRoute('index'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
/* | ||
What this does | ||
This controller handles the `/download-calculations` route. | ||
It fetches all calculations from the database using the findAll() method. | ||
We then loop over each calculation and add it to a CSV. | ||
We then download that CSV. | ||
*/ | ||
|
||
namespace App\Controller; | ||
|
||
use App\Entity\VatCalculation; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
class DownloadCalculationsController extends AbstractController | ||
{ | ||
public function new(EntityManagerInterface $entityManager): Response | ||
{ | ||
$calculations = $entityManager->getRepository(VatCalculation::class)->findAll(); | ||
|
||
$csvData = []; | ||
// Set up the column titles | ||
$csvData[] = [ | ||
'ID', | ||
'Input: Monetary Value', | ||
'Input: VAT Rate', | ||
'Input: Is VAT included?', | ||
'Output: Monetary value excluding VAT', | ||
'Output: Amount of VAT', | ||
'Output: Monetary value including VAT' | ||
]; | ||
|
||
foreach ($calculations as $calculation) { | ||
// Add the $calculation to the CSV | ||
$csvData[] = [ | ||
$calculation->getId(), | ||
$calculation->getV(), | ||
$calculation->getR(), | ||
$calculation->getIsVatIncluded(), | ||
$calculation->getResultWithoutVat(), | ||
$calculation->getAmountOfVat(), | ||
$calculation->getResultWithVat() | ||
]; | ||
} | ||
$entityManager->flush(); | ||
|
||
// Download the CSV | ||
$csvFileName = 'vat-calculations.csv'; | ||
$csvFile = fopen($csvFileName, 'w'); | ||
foreach ($csvData as $csvRow) { | ||
fputcsv($csvFile, $csvRow); | ||
} | ||
fclose($csvFile); | ||
|
||
$response = new Response(); | ||
$response->headers->set('Content-Type', 'text/csv'); | ||
$response->headers->set('Content-Disposition', 'attachment; filename="' . $csvFileName . '"'); | ||
$response->setContent(file_get_contents($csvFileName)); | ||
|
||
return $response; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
/* | ||
What this does | ||
This controller handles the `/` route. | ||
It fetches all calculations from the database using the findAll() method. | ||
We pass this array through to the twig template. | ||
*/ | ||
|
||
namespace App\Controller; | ||
|
||
use App\Entity\VatCalculation; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
class IndexController extends AbstractController | ||
{ | ||
public function new(EntityManagerInterface $entityManager, Request $request): Response | ||
{ | ||
$calculations = $entityManager->getRepository(VatCalculation::class)->findAll(); | ||
/* | ||
We can pass a highlighted VatCalculation through from the form | ||
to show a user what they have just submitted. | ||
If we do get a highlightId in the query string, we try to find this calculation from the database. | ||
This ensures that only ids that are associated with a calculation get passed through. | ||
We then pass it through to the twig template to let it be highlighted | ||
*/ | ||
$highlightId = $request->query->get('highlightId'); | ||
|
||
$highlightedCalculation = null; | ||
if ($highlightId !== null) { | ||
$highlightedCalculation = $entityManager->getRepository(VatCalculation::class)->find($highlightId); | ||
} | ||
|
||
return $this->render('index.html.twig', [ | ||
'calculations' => $calculations, | ||
'highlightedCalculation' => $highlightedCalculation, | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
/* | ||
What this does | ||
This controller handles the /new-calculation route. | ||
We create the form from the CalculateVatForm class and either return the rendered form or process the form. | ||
We call handleRequest on the form object which validates the inputs. | ||
If the inputs are invalid, it returns the form with the appropriate error messages. | ||
If the inputs are valid, it gets the submitted data from the form, which is a new instance of the VatCalculation class. | ||
Then it passes it through to the EntityManagerInterface to persist the data in the database. | ||
SQL Injection | ||
We handle the isValid() method on the form which only allows data to be persisted if it passes validation. | ||
*/ | ||
|
||
namespace App\Controller; | ||
|
||
use App\Form\CalculateVatForm; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
class NewCalculationController extends AbstractController | ||
{ | ||
public function new(Request $request, EntityManagerInterface $entityManager): Response | ||
{ | ||
$form = $this->createForm(CalculateVatForm::class); | ||
|
||
$form->handleRequest($request); | ||
|
||
if ($form->isSubmitted() && $form->isValid()) { | ||
$vatCalculation = $form->getData(); | ||
$entityManager->persist($vatCalculation); | ||
$entityManager->flush(); | ||
// Pass this calculation to IndexController so that we can highlight it | ||
return $this->redirectToRoute('index', ['highlightId' => $vatCalculation->getId()]); | ||
} | ||
|
||
return $this->render('calculateVat.html.twig', [ | ||
'form' => $form->createView(), | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<?php | ||
/* | ||
What this does | ||
This is a class of VAT Calculation. | ||
We use a special commented markup for Doctrine which handles database mapping for us. | ||
Because this is in the entity folder, we can run `php bin/console make:migration` to make a migration. | ||
We can then run `php bin/console doctrine:migrations:migrate` to perform the migrations. | ||
A migration makes sure our database has the correct columns to handle this data. | ||
SQL Injection | ||
Doctrine handles SQL parameterization, making SQL injection attacks less likely | ||
*/ | ||
|
||
namespace App\Entity; | ||
|
||
use Doctrine\ORM\Mapping as ORM; | ||
|
||
/** | ||
* @ORM\Entity() | ||
*/ | ||
class VatCalculation | ||
{ | ||
/** | ||
* @ORM\Id() | ||
* @ORM\GeneratedValue() | ||
* @ORM\Column(type="integer") | ||
*/ | ||
private $id; | ||
|
||
/** | ||
* @ORM\Column(type="float") | ||
*/ | ||
private $v; | ||
|
||
/** | ||
* @ORM\Column(type="float") | ||
*/ | ||
private $r; | ||
|
||
/** | ||
* @ORM\Column(type="boolean") | ||
*/ | ||
private $isVatIncluded; | ||
|
||
// Getters and setters | ||
|
||
public function getId(): ?int | ||
{ | ||
return $this->id; | ||
} | ||
|
||
public function getV(): ?float | ||
{ | ||
return $this->v; | ||
} | ||
|
||
public function setV(float $v): self | ||
{ | ||
$this->v = $v; | ||
|
||
return $this; | ||
} | ||
|
||
public function getR(): ?float | ||
{ | ||
return $this->r; | ||
} | ||
|
||
public function setR(float $r): self | ||
{ | ||
$this->r = $r; | ||
|
||
return $this; | ||
} | ||
|
||
public function getIsVatIncluded(): ?bool | ||
{ | ||
return $this->isVatIncluded; | ||
} | ||
|
||
public function setIsVatIncluded(bool $isVatIncluded): self | ||
{ | ||
$this->isVatIncluded = $isVatIncluded; | ||
|
||
return $this; | ||
} | ||
|
||
/* | ||
We perform the calculations on the fly rather than storing them in the database. | ||
This allows us to add or change calculations in the future without having to update all items in the database. | ||
*/ | ||
|
||
public function getResultWithVat(): ?float | ||
{ | ||
/* | ||
If VAT is included in the original value, we just return the original value rounded to 2 decimal places. | ||
We use a return early style to make the code more readable. | ||
*/ | ||
if ($this->isVatIncluded) { | ||
return round($this->getV(), 2); | ||
} | ||
/* | ||
If VAT is not included, we work out the amount to add, which is v * r * .01. | ||
We're asking for the percentage so have to times it by .01 to get a decimal. | ||
We round this to 2 decimal places too. | ||
*/ | ||
return round($this->getV() + $this->getV() * $this->getR() * .01, 2); | ||
} | ||
|
||
public function getResultWithoutVat(): ?float | ||
{ | ||
/* | ||
If VAT is included, we divide the original value by 1 + percentage of VAT. | ||
We round the result. | ||
*/ | ||
if ($this->isVatIncluded) { | ||
return round($this->getV() / (1 + $this->getR() * .01), 2); | ||
} | ||
/* | ||
If VAT is not included, we can just return the original value. | ||
*/ | ||
return round($this->getV(), 2); | ||
} | ||
|
||
public function getAmountOfVat(): ?float | ||
{ | ||
/* | ||
The amount of VAT is just the result with VAT minus the result without. | ||
*/ | ||
return round($this->getResultWithVat() - $this->getResultWithoutVat(), 2); | ||
} | ||
} |
Oops, something went wrong.