diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 8f9d1586..44ea77f3 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -32,4 +32,5 @@ doctrine: dql: string_functions: - regexp: DoctrineExtensions\Query\Mysql\Regexp \ No newline at end of file + regexp: DoctrineExtensions\Query\Mysql\Regexp + ifnull: DoctrineExtensions\Query\Mysql\IfNull \ No newline at end of file diff --git a/src/DataTables/Filters/Constraints/FilterTrait.php b/src/DataTables/Filters/Constraints/FilterTrait.php index 275fb17a..5371a48d 100644 --- a/src/DataTables/Filters/Constraints/FilterTrait.php +++ b/src/DataTables/Filters/Constraints/FilterTrait.php @@ -2,6 +2,7 @@ namespace App\DataTables\Filters\Constraints; +use Doctrine\DBAL\ParameterType; use Doctrine\ORM\QueryBuilder; trait FilterTrait diff --git a/src/DataTables/Filters/Constraints/NumberConstraint.php b/src/DataTables/Filters/Constraints/NumberConstraint.php index fa1b073d..bacd289a 100644 --- a/src/DataTables/Filters/Constraints/NumberConstraint.php +++ b/src/DataTables/Filters/Constraints/NumberConstraint.php @@ -2,6 +2,7 @@ namespace App\DataTables\Filters\Constraints; +use Doctrine\DBAL\ParameterType; use Doctrine\ORM\QueryBuilder; use \RuntimeException; diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index ea3d4c64..df5c976b 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -19,7 +19,6 @@ use App\Entity\Parts\Storelocation; use App\Entity\Parts\Supplier; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\QueryBuilder; -use Svg\Tag\Text; class PartFilter implements FilterInterface { @@ -83,6 +82,9 @@ class PartFilter implements FilterInterface /** @var IntConstraint */ protected $lotCount; + /** @var NumberConstraint */ + protected $amountSum; + /** @var BooleanConstraint */ protected $lotNeedsRefill; @@ -127,7 +129,12 @@ class PartFilter implements FilterInterface $this->addedDate = new DateTimeConstraint('part.addedDate'); $this->lastModified = new DateTimeConstraint('part.lastModified'); - $this->minAmount = new NumberConstraint('part.minAmount'); + $this->minAmount = new NumberConstraint('part.minamount'); + /* We have to use an IntConstraint here because otherwise we get just an empty result list when applying the filter + This seems to be related to the fact, that PDO does not have an float parameter type and using string type does not work in this situation (at least in SQLite) + TODO: Find a better solution here + */ + $this->amountSum = new IntConstraint('amountSum'); $this->lotCount = new IntConstraint('COUNT(partLots)'); $this->supplier = new EntityConstraint($nodesListBuilder, Supplier::class, 'orderdetails.supplier'); $this->lotNeedsRefill = new BooleanConstraint('partLots.needs_refill'); @@ -360,6 +367,10 @@ class PartFilter implements FilterInterface return $this->manufacturing_status; } + public function getAmountSum(): NumberConstraint + { + return $this->amountSum; + } } diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index 3cca6ba2..cc0fe1b2 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -53,6 +53,7 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; use App\Entity\Parts\Storelocation; use App\Entity\Parts\Supplier; use App\Services\AmountFormatter; @@ -232,6 +233,7 @@ final class PartsDataTable implements DataTableTypeInterface return $this->amountFormatter->format($amount, $context->getPartUnit()); }, + 'orderField' => 'amountSum' ]) ->add('minamount', TextColumn::class, [ 'label' => $this->translator->trans('part.table.minamount'), @@ -326,6 +328,7 @@ final class PartsDataTable implements DataTableTypeInterface private function getQuery(QueryBuilder $builder): void { + $builder->distinct()->select('part') ->addSelect('category') ->addSelect('footprint') @@ -337,6 +340,16 @@ final class PartsDataTable implements DataTableTypeInterface ->addSelect('orderdetails') ->addSelect('attachments') ->addSelect('storelocations') + //Calculate amount sum using a subquery, so we can filter and sort by it + ->addSelect( + '( + SELECT IFNULL(SUM(partLot.amount), 0.0) + FROM '. PartLot::class. ' partLot + WHERE partLot.part = part.id + AND partLot.instock_unknown = false + AND (partLot.expiration_date IS NULL OR partLot.expiration_date > CURRENT_DATE()) + ) AS HIDDEN amountSum' + ) ->from(Part::class, 'part') ->leftJoin('part.category', 'category') ->leftJoin('part.master_picture_attachment', 'master_picture_attachment') diff --git a/src/Form/Filters/PartFilterType.php b/src/Form/Filters/PartFilterType.php index c7894371..968187ac 100644 --- a/src/Form/Filters/PartFilterType.php +++ b/src/Form/Filters/PartFilterType.php @@ -173,6 +173,11 @@ class PartFilterType extends AbstractType 'step' => 1, ]); + $builder->add('amountSum', NumberConstraintType::class, [ + 'label' => 'part.filter.amount_sum', + 'min' => 0, + ]); + $builder->add('lotNeedsRefill', BooleanConstraintType::class, [ 'label' => 'part.filter.lotNeedsRefill' ]); diff --git a/templates/Parts/lists/_filter.html.twig b/templates/Parts/lists/_filter.html.twig index 254298b6..953ce0d6 100644 --- a/templates/Parts/lists/_filter.html.twig +++ b/templates/Parts/lists/_filter.html.twig @@ -54,6 +54,7 @@