Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rimi-itk committed Jun 3, 2024
2 parents 44a569d + c7884fc commit fb545de
Show file tree
Hide file tree
Showing 23 changed files with 550 additions and 42 deletions.
36 changes: 35 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ API_SERVICE_SPRINT_NAME_REGEX="/(?<weeks>(?:-?\d+-?)*)\.(?<year>\d+)$/"
APP_WEEK_GOAL_LOW=25.0
APP_WEEK_GOAL_HIGH=34.5
APP_INVOICE_SUPPLIER_ACCOUNT=APP_INVOICE_SUPPLIER_ACCOUNT
APP_INVOICE_RECEIVER_DEFAULT_ACCOUNT=APP_INVOICE_DEFAULT_RECEIVER_ACCOUNT
APP_INVOICE_DESCRIPTION_TEMPLATE="Spørgsmål vedrørende fakturaen rettes til %name%, %email%."
APP_PROJECT_BILLING_DEFAULT_DESCRIPTION=
APP_DEFAULT_PLANNING_DATA_PROVIDER=
Expand Down Expand Up @@ -56,3 +55,38 @@ OIDC_CLI_LOGIN_ROUTE=index
CLIENT_STANDARD_PRICE=705.00

DEFAULT_URI=https://economics.local.itkdev.dk/

PRODUCT_QUANTITY_SCALE=2

# Invoice entry accounts.
# Must be a valid JSON object mapping account IDs to a account metadata.
#
# Requirements;
#
# * At least one account must be defined.
# * "label" is required.
# * One and only one account must be "default" (a single account will be
# "default")
# * If more than one account is defined, one and only one account must be
# "product".
# * The "product" account cannot be "default"
#
# INVOICE_ENTRY_ACCOUNTS='{
# "test": {
# "label": "Test account"
# },
# "account-87": {
# "label": "The default account",
# "default": true
# },
# "product": {
# "label": "The real PSP element",
# "product": true
# }
# }'
#
INVOICE_ENTRY_ACCOUNTS='{
"Define INVOICE_ENTRY_ACCOUNTS in .env.local": {
"label": "Define INVOICE_ENTRY_ACCOUNTS in .env.local"
}
}'
7 changes: 6 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
DATABASE_URL="mysql://root:password@mariadb:3306/db_test?serverVersion=10.9.3-MariaDB&charset=utf8mb4"

APP_INVOICE_SUPPLIER_ACCOUNT=1111
APP_INVOICE_RECEIVER_DEFAULT_ACCOUNT="XG-0000000000-00000"
APP_PROJECT_BILLING_DEFAULT_DESCRIPTION="Beskrivelse"

INVOICE_ENTRY_ACCOUNTS='{
"test": {
"label": "test"
}
}'
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.3.0] - 2024-06-03

* [PR-126](https://github.com/itk-dev/economics/pull/126)
1590: Added worklog product as prefix on product invoice entries
* [PR-125](https://github.com/itk-dev/economics/pull/125)
1547: Set account based on invoice entry type
* [PR-123](https://github.com/itk-dev/economics/pull/123)
1544: Allowed invoicing issues with products and no worklogs
* [PR-122](https://github.com/itk-dev/economics/pull/122)
1547: Added invoice entry account selector
* [PR-121](https://github.com/itk-dev/economics/pull/121)
1485: Fixed floating number issues
* [PR-120](https://github.com/itk-dev/economics/pull/120)
1484: Cleaned up worklog cleanup
* [PR-118](https://github.com/itk-dev/economics/pull/118)
1485: Made product quantity floatable

## [2.2.0] - 2024-05-06

* [PR-114](https://github.com/itk-dev/economics/pull/114)
Expand Down Expand Up @@ -242,7 +259,8 @@ complete process.
* Updated to authorization code flow.
* Changed worklog save button styling to be sticky.

[Unreleased]: https://github.com/itk-dev/economics/compare/2.2.0...HEAD
[Unreleased]: https://github.com/itk-dev/economics/compare/2.3.0...HEAD
[2.3.0]: https://github.com/itk-dev/economics/compare/2.2.0...2.3.0
[2.2.0]: https://github.com/itk-dev/economics/compare/2.1.2...2.2.0
[2.1.2]: https://github.com/itk-dev/economics/compare/2.1.1...2.1.2
[2.1.1]: https://github.com/itk-dev/economics/compare/2.1.0...2.1.1
Expand Down
1 change: 1 addition & 0 deletions config/packages/twig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ twig:
format: 'd.m.Y'
globals:
view_controller: '@App\Controller\ViewController'
invoice_entry_helper: '@App\Service\InvoiceEntryHelper'

when@test:
twig:
Expand Down
11 changes: 10 additions & 1 deletion config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ services:
bind:
$defaultInvoiceDescriptionTemplate: '%env(APP_INVOICE_DESCRIPTION_TEMPLATE)%'
$invoiceSupplierAccount: '%env(string:APP_INVOICE_SUPPLIER_ACCOUNT)%'
$invoiceDefaultReceiverAccount: '%env(string:APP_INVOICE_RECEIVER_DEFAULT_ACCOUNT)%'
$projectBillingDefaultDescription: '%env(string:APP_PROJECT_BILLING_DEFAULT_DESCRIPTION)%'
$planningDefaultDataProvider: '%env(string:APP_DEFAULT_PLANNING_DATA_PROVIDER)%'

Expand Down Expand Up @@ -49,3 +48,13 @@ services:
$options:
standard_price: '%env(float:CLIENT_STANDARD_PRICE)%'

App\Controller\IssueController:
arguments:
$options:
issue_product_type_options:
quantity_scale: '%env(int:PRODUCT_QUANTITY_SCALE)%'

App\Service\InvoiceEntryHelper:
arguments:
$options:
accounts: '%env(json:INVOICE_ENTRY_ACCOUNTS)%'
31 changes: 31 additions & 0 deletions migrations/Version20240521135612.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?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 Version20240521135612 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('ALTER TABLE issue_product CHANGE quantity quantity DOUBLE PRECISION NOT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE issue_product CHANGE quantity quantity INT NOT NULL');
}
}
35 changes: 35 additions & 0 deletions migrations/Version20240530115938.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?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 Version20240530115938 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('ALTER TABLE issue_product ADD invoice_entry_id INT DEFAULT NULL, ADD is_billed TINYINT(1) DEFAULT NULL');
$this->addSql('ALTER TABLE issue_product ADD CONSTRAINT FK_76B2414CA51E131A FOREIGN KEY (invoice_entry_id) REFERENCES invoice_entry (id) ON DELETE SET NULL');
$this->addSql('CREATE INDEX IDX_76B2414CA51E131A ON issue_product (invoice_entry_id)');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE issue_product DROP FOREIGN KEY FK_76B2414CA51E131A');
$this->addSql('DROP INDEX IDX_76B2414CA51E131A ON issue_product');
$this->addSql('ALTER TABLE issue_product DROP invoice_entry_id, DROP is_billed');
}
}
49 changes: 49 additions & 0 deletions migrations/Version20240531125801.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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 Version20240531125801 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// Add product from preceding worklog invoice entry as prefix on product entries.
$this->addSql(<<<'SQL'
UPDATE
invoice_entry
SET
product = CONCAT(
IF(
ISNULL(
-- Get "product" from preceding worklog entry (this expression is repeated below).
(SELECT product FROM invoice_entry AS ie WHERE ie.invoice_id = invoice_entry.invoice_id AND ie.entry_type = 'worklog' AND ie.entry_index < invoice_entry.entry_index ORDER BY ie.entry_index DESC LIMIT 1)
),
'',
CONCAT(
(SELECT product FROM invoice_entry AS ie WHERE ie.invoice_id = invoice_entry.invoice_id AND ie.entry_type = 'worklog' AND ie.entry_index < invoice_entry.entry_index ORDER BY ie.entry_index DESC LIMIT 1),
': '
)
),
product
)
WHERE entry_type = 'product'
SQL);
}

public function down(Schema $schema): void
{
// There is not going back (or down)!
}
}
5 changes: 4 additions & 1 deletion src/Controller/InvoiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use App\Repository\InvoiceRepository;
use App\Service\BillingService;
use App\Service\ClientHelper;
use App\Service\InvoiceEntryHelper;
use App\Service\ViewService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
Expand All @@ -37,6 +38,7 @@ public function __construct(
private readonly BillingService $billingService,
private readonly TranslatorInterface $translator,
private readonly ViewService $viewService,
private readonly InvoiceEntryHelper $invoiceEntryHelper,
) {
}

Expand All @@ -60,13 +62,14 @@ public function index(Request $request, InvoiceRepository $invoiceRepository): R

#[Route('/new', name: 'app_invoices_new', methods: ['GET', 'POST'])]
#[IsGranted('EDIT')]
public function new(Request $request, InvoiceRepository $invoiceRepository, string $invoiceDefaultReceiverAccount): Response
public function new(Request $request, InvoiceRepository $invoiceRepository): Response
{
$invoice = new Invoice();
$form = $this->createForm(InvoiceNewType::class, $invoice);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$invoiceDefaultReceiverAccount = $this->invoiceEntryHelper->getDefaultAccount();
if (!empty($invoiceDefaultReceiverAccount)) {
$invoice->setDefaultReceiverAccount($invoiceDefaultReceiverAccount);
}
Expand Down
12 changes: 9 additions & 3 deletions src/Controller/InvoiceEntryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use App\Repository\InvoiceEntryRepository;
use App\Service\BillingService;
use App\Service\ClientHelper;
use App\Service\InvoiceEntryHelper;
use App\Service\ViewService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -26,7 +27,8 @@ public function __construct(
private readonly BillingService $billingService,
private readonly TranslatorInterface $translator,
private readonly ViewService $viewService,
private readonly ClientHelper $clientHelper
private readonly ClientHelper $clientHelper,
private readonly InvoiceEntryHelper $invoiceEntryHelper,
) {
}

Expand Down Expand Up @@ -91,10 +93,14 @@ public function edit(Request $request, Invoice $invoice, InvoiceEntry $invoiceEn
$options['disabled'] = true;
}

if (InvoiceEntryTypeEnum::WORKLOG == $invoiceEntry->getEntryType()) {
$accounts = $this->invoiceEntryHelper->getAccountOptions($invoiceEntry->getAccount());
if (!empty($accounts)) {
$options['invoice_entry_accounts'] = $accounts;
}
if (InvoiceEntryTypeEnum::WORKLOG === $invoiceEntry->getEntryType()) {
$form = $this->createForm(InvoiceEntryWorklogType::class, $invoiceEntry, $options);
} else {
$form = $this->createForm(InvoiceEntryType::class, $invoiceEntry);
$form = $this->createForm(InvoiceEntryType::class, $invoiceEntry, $options);
}

$form->handleRequest($request);
Expand Down
6 changes: 4 additions & 2 deletions src/Controller/IssueController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class IssueController extends AbstractController
{
public function __construct(
private readonly ViewService $viewService,
private readonly array $options,
) {
}

Expand All @@ -47,6 +48,7 @@ public function index(Request $request, Project $project, IssueRepository $issue
#[Route('/{id}', name: 'show', methods: [Request::METHOD_GET])]
public function show(Project $project, Issue $issue): Response
{
$issueProductTypeOptions = $this->options['issue_product_type_options'] ?? [];
$product = (new IssueProduct())
->setIssue($issue);
$addProductForm = $project->getProducts()->isEmpty()
Expand All @@ -58,7 +60,7 @@ public function show(Project $project, Issue $issue): Response
'id' => $issue->getId(),
]),
'method' => Request::METHOD_POST,
]);
] + $issueProductTypeOptions);

// Index issue products by id.
$products = [];
Expand All @@ -77,7 +79,7 @@ public function show(Project $project, Issue $issue): Response
'product' => $product->getId(),
]),
'method' => Request::METHOD_PUT,
])
] + $issueProductTypeOptions)
->createView(),
$products
);
Expand Down
34 changes: 34 additions & 0 deletions src/Entity/InvoiceEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ class InvoiceEntry extends AbstractBaseEntity
#[ORM\OneToMany(mappedBy: 'invoiceEntry', targetEntity: Worklog::class)]
private Collection $worklogs;

#[ORM\OneToMany(mappedBy: 'invoiceEntry', targetEntity: IssueProduct::class)]
private Collection $issueProducts;

public function __construct()
{
$this->worklogs = new ArrayCollection();
$this->issueProducts = new ArrayCollection();
}

public function getInvoice(): ?Invoice
Expand Down Expand Up @@ -182,6 +186,36 @@ public function removeWorklog(Worklog $worklog): self
return $this;
}

/**
* @return Collection<int, IssueProduct>
*/
public function getIssueProducts(): Collection
{
return $this->issueProducts;
}

public function addIssueProduct(IssueProduct $issueProduct): self
{
if (!$this->issueProducts->contains($issueProduct)) {
$this->issueProducts->add($issueProduct);
$issueProduct->setInvoiceEntry($this);
}

return $this;
}

public function removeIssueProduct(IssueProduct $issueProduct): self
{
if ($this->issueProducts->removeElement($issueProduct)) {
// set the owning side to null (unless already changed)
if ($issueProduct->getInvoiceEntry() === $this) {
$issueProduct->setInvoiceEntry(null);
}
}

return $this;
}

public function getEntryType(): InvoiceEntryTypeEnum
{
return $this->entryType;
Expand Down
Loading

0 comments on commit fb545de

Please sign in to comment.