From 658c8d30e690e9c67c94331dd94a1efe52eeed3d Mon Sep 17 00:00:00 2001 From: Fabian Wunsch Date: Thu, 5 Sep 2024 15:41:00 +0200 Subject: [PATCH 1/3] First order details draft --- migrations/Version20240905085300.php | 38 ++++++++++++++++++ .../Part/LessThanDesiredConstraint.php | 4 +- src/DataTables/Filters/PartFilter.php | 4 ++ src/DataTables/PartsDataTable.php | 9 +++++ src/Entity/Parts/Part.php | 34 +++++++++++++++- src/Entity/Parts/PartTraits/InstockTrait.php | 40 ++++++++++++++++++- src/Form/Filters/PartFilterType.php | 10 +++++ src/Form/Part/PartBaseType.php | 16 ++++++++ src/Serializer/PartNormalizer.php | 3 ++ .../LabelSystem/SandboxedTwigFactory.php | 2 +- templates/parts/edit/_main.html.twig | 2 + templates/parts/info/_main_infos.html.twig | 13 +++++- templates/parts/lists/_filter.html.twig | 4 +- 13 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 migrations/Version20240905085300.php diff --git a/migrations/Version20240905085300.php b/migrations/Version20240905085300.php new file mode 100644 index 000000000..4c3d45718 --- /dev/null +++ b/migrations/Version20240905085300.php @@ -0,0 +1,38 @@ +addSql('ALTER TABLE parts ADD orderamount DOUBLE PRECISION NOT NULL DEFAULT 0, ADD orderDate DATETIME'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `parts` DROP orderamount, DROP orderDate'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('ALTER TABLE parts ADD COLUMN orderamount DOUBLE PRECISION NOT NULL DEFAULT 0'); + $this->addSql('ALTER TABLE parts ADD COLUMN orderDate DATETIME'); + } + + public function sqLiteDown(Schema $schema): void + { + $error; + // TODO: implement backwards migration for SQlite + } +} diff --git a/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php index eb96ad33c..78102fe1a 100644 --- a/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php +++ b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php @@ -48,9 +48,9 @@ public function apply(QueryBuilder $queryBuilder): void //If value is true, we want to filter for parts with stock < desired stock if ($this->value) { - $queryBuilder->andHaving( $this->property . ' < part.minamount'); + $queryBuilder->andHaving($this->property . ' + part.orderamount < part.minamount'); } else { - $queryBuilder->andHaving($this->property . ' >= part.minamount'); + $queryBuilder->andHaving($this->property . ' + part.orderamount >= part.minamount'); } } } diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index ff98c76f9..4f2c9e57f 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -58,6 +58,8 @@ class PartFilter implements FilterInterface public readonly TextConstraint $comment; public readonly TagsConstraint $tags; public readonly NumberConstraint $minAmount; + public readonly NumberConstraint $orderAmount; + public readonly DateTimeConstraint $orderDelivery; public readonly BooleanConstraint $favorite; public readonly BooleanConstraint $needsReview; public readonly NumberConstraint $mass; @@ -120,6 +122,8 @@ public function __construct(NodesListBuilder $nodesListBuilder) $this->lastModified = new DateTimeConstraint('part.lastModified'); $this->minAmount = new NumberConstraint('part.minamount'); + $this->orderAmount = new NumberConstraint('part.orderamount'); + $this->orderDelivery = new DateTimeConstraint('part.orderDelivery'); /* 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 diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index 28c564b11..afb41269e 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -151,6 +151,15 @@ public function configure(DataTable $dataTable, array $options): void 'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format($value, $context->getPartUnit())), ]) + ->add('orderamount', TextColumn::class, [ + 'label' => $this->translator->trans('part.table.orderamount'), + 'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format($value, + $context->getPartUnit())), + ]) + ->add('orderDelivery', LocaleDateTimeColumn::class, [ + 'label' => $this->translator->trans('part.table.orderDelivery'), + 'timeFormat' => 'none', + ]) ->add('partUnit', TextColumn::class, [ 'label' => $this->translator->trans('part.table.partUnit'), 'orderField' => 'NATSORT(_partUnit.name)', diff --git a/src/Entity/Parts/Part.php b/src/Entity/Parts/Part.php index 4addf503c..589372d4e 100644 --- a/src/Entity/Parts/Part.php +++ b/src/Entity/Parts/Part.php @@ -59,6 +59,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Doctrine\DBAL\Types\Types; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -99,9 +100,9 @@ #[ApiFilter(PartStoragelocationFilter::class, properties: ["storage_location"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment", "description", "ipn", "tags", "manufacturer_product_number"])] #[ApiFilter(BooleanFilter::class, properties: ["favorite" , "needs_review"])] -#[ApiFilter(RangeFilter::class, properties: ["mass", "minamount"])] +#[ApiFilter(RangeFilter::class, properties: ["mass", "minamount", "orderamount"])] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)] -#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'orderDelivery', 'addedDate', 'lastModified'])] class Part extends AttachmentContainingDBElement { use AdvancedPropertyTrait; @@ -124,6 +125,15 @@ class Part extends AttachmentContainingDBElement #[UniqueObjectCollection(fields: ['name', 'group', 'element'])] protected Collection $parameters; + /** + * @var \DateTimeInterface|null Set a time when the new order will arive. + * Set to null, if there is no known date or no order. + */ + #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] + #[ORM\Column(name: 'orderDelivery', type: Types::DATETIME_MUTABLE, nullable: true)] + #[Year2038BugWorkaround] + protected ?\DateTimeInterface $orderDelivery = null; + /** ************************************************************* * Overridden properties @@ -214,6 +224,26 @@ public function __clone() parent::__clone(); } + /** + * Gets the expected delivery date of the part. Returns null, if no delivery is due. + */ + public function getOrderDelivery(): ?\DateTimeInterface + { + return $this->orderDelivery; + } + + /** + * Sets the expected delivery date of the part. Set to null, if no delivery is due. + * + * + */ + public function setOrderDelivery(?\DateTimeInterface $orderDelivery): self + { + $this->orderDelivery = $orderDelivery; + + return $this; + } + #[Assert\Callback] public function validate(ExecutionContextInterface $context, $payload): void { diff --git a/src/Entity/Parts/PartTraits/InstockTrait.php b/src/Entity/Parts/PartTraits/InstockTrait.php index 08b070f3a..e616b7c36 100644 --- a/src/Entity/Parts/PartTraits/InstockTrait.php +++ b/src/Entity/Parts/PartTraits/InstockTrait.php @@ -55,6 +55,14 @@ trait InstockTrait #[ORM\Column(type: Types::FLOAT)] protected float $minamount = 0; + /** + * @var float The number of already ordered units + */ + #[Assert\PositiveOrZero] + #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] + #[ORM\Column(type: Types::FLOAT)] + protected float $orderamount = 0; + /** * @var ?MeasurementUnit the unit in which the part's amount is measured */ @@ -137,6 +145,21 @@ public function getMinAmount(): float return round($this->minamount); } + /** + * Get the count of parts which are already ordered. + * If an integer-based part unit is selected, the value will be rounded to integers. + * + * @return float count of parts which are already ordered + */ + public function getOrderAmount(): float + { + if ($this->useFloatAmount()) { + return $this->orderamount; + } + + return round($this->orderamount); + } + /** * Checks if this part uses the float amount . * This setting is based on the part unit (see MeasurementUnit->isInteger()). @@ -158,7 +181,7 @@ public function useFloatAmount(): bool */ public function isNotEnoughInstock(): bool { - return $this->getAmountSum() < $this->getMinAmount(); + return ($this->getAmountSum() + $this->getOrderAmount()) < $this->getMinAmount(); } /** @@ -238,4 +261,19 @@ public function setMinAmount(float $new_minamount): self return $this; } + + /** + * Set the amount of already ordered parts. + * See getPartUnit() for the associated unit. + * + * @param float $new_orderamount the new count of parts are already ordered + * + * @return $this + */ + public function setOrderAmount(float $new_orderamount): self + { + $this->orderamount = $new_orderamount; + + return $this; + } } diff --git a/src/Form/Filters/PartFilterType.php b/src/Form/Filters/PartFilterType.php index dfe449d18..ed38e2f2b 100644 --- a/src/Form/Filters/PartFilterType.php +++ b/src/Form/Filters/PartFilterType.php @@ -202,6 +202,16 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'min' => 0, ]); + $builder->add('orderAmount', NumberConstraintType::class, [ + 'label' => 'part.edit.orderstock', + 'min' => 0, + ]); + + $builder->add('orderDelivery', DateTimeConstraintType::class, [ + 'label' => 'part.edit.orderDelivery', + 'input_type' => DateType::class, + ]); + $builder->add('lotCount', NumberConstraintType::class, [ 'label' => 'part.filter.lot_count', 'min' => 0, diff --git a/src/Form/Part/PartBaseType.php b/src/Form/Part/PartBaseType.php index 4a71f03ad..1f7d274b9 100644 --- a/src/Form/Part/PartBaseType.php +++ b/src/Form/Part/PartBaseType.php @@ -44,6 +44,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; +use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Extension\Core\Type\ResetType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -95,6 +96,21 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'part.edit.mininstock', 'measurement_unit' => $part->getPartUnit(), ]) + ->add('orderAmount', SIUnitType::class, [ + 'attr' => [ + 'min' => 0, + 'placeholder' => 'part.editmininstock.placeholder', + ], + 'label' => 'part.edit.orderstock', + 'measurement_unit' => $part->getPartUnit(), + ]) + ->add('orderDelivery', DateType::class, [ + 'label' => 'part.edit.orderDelivery', + 'attr' => [], + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'required' => false, + ]) ->add('category', StructuralEntityType::class, [ 'class' => Category::class, 'allow_add' => $this->security->isGranted('@categories.create'), diff --git a/src/Serializer/PartNormalizer.php b/src/Serializer/PartNormalizer.php index 650b0214e..1e80d5c57 100644 --- a/src/Serializer/PartNormalizer.php +++ b/src/Serializer/PartNormalizer.php @@ -129,6 +129,9 @@ public function denormalize($data, string $type, string $format = null, array $c if (empty($data['minamount'])) { $data['minamount'] = 0.0; } + if (empty($data['orderamount'])) { + $data['orderamount'] = 0.0; + } $context[self::ALREADY_CALLED] = true; diff --git a/src/Services/LabelSystem/SandboxedTwigFactory.php b/src/Services/LabelSystem/SandboxedTwigFactory.php index cdf0594f7..5a97938e8 100644 --- a/src/Services/LabelSystem/SandboxedTwigFactory.php +++ b/src/Services/LabelSystem/SandboxedTwigFactory.php @@ -133,7 +133,7 @@ final class SandboxedTwigFactory Supplier::class => ['getShippingCosts', 'getDefaultCurrency'], Part::class => ['isNeedsReview', 'getTags', 'getMass', 'getIpn', 'getProviderReference', 'getDescription', 'getComment', 'isFavorite', 'getCategory', 'getFootprint', - 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum', + 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getOrderAmount', 'getOrderDate', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum', 'getManufacturerProductUrl', 'getCustomProductURL', 'getManufacturingStatus', 'getManufacturer', 'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete', 'getParameters', 'getGroupedParameters', diff --git a/templates/parts/edit/_main.html.twig b/templates/parts/edit/_main.html.twig index f153d878a..22a0c5647 100644 --- a/templates/parts/edit/_main.html.twig +++ b/templates/parts/edit/_main.html.twig @@ -10,6 +10,8 @@ {{ form_row(form.category) }} {{ form_row(form.tags) }} {{ form_row(form.minAmount) }} +{{ form_row(form.orderAmount) }} +{{ form_row(form.orderDate) }} {{ form_row(form.footprint) }} diff --git a/templates/parts/info/_main_infos.html.twig b/templates/parts/info/_main_infos.html.twig index 7bcdd3558..86f2d06e5 100644 --- a/templates/parts/info/_main_infos.html.twig +++ b/templates/parts/info/_main_infos.html.twig @@ -55,6 +55,17 @@ {% if part.expiredAmountSum > 0 %} (+{{ part.expiredAmountSum }}) {% endif %} + {% if part.orderAmount > 0 %} + (+ + {{ part.orderAmount | format_amount(part.partUnit) }} + {% if part.orderDate %} + @ + + {{ part.orderDate | format_date() }}
+
+ {% endif %} + ) + {% endif %} / {{ part.minAmount | format_amount(part.partUnit) }} @@ -93,4 +104,4 @@ {% endif %} #} - \ No newline at end of file + diff --git a/templates/parts/lists/_filter.html.twig b/templates/parts/lists/_filter.html.twig index c29e8ecdc..f578f0df1 100644 --- a/templates/parts/lists/_filter.html.twig +++ b/templates/parts/lists/_filter.html.twig @@ -66,6 +66,8 @@
{{ form_row(filterForm.storelocation) }} {{ form_row(filterForm.minAmount) }} + {{ form_row(filterForm.orderAmount) }} + {{ form_row(filterForm.orderDelivery) }} {{ form_row(filterForm.amountSum) }} {{ form_row(filterForm.lessThanDesired) }} {{ form_row(filterForm.lotCount) }} @@ -150,4 +152,4 @@ {{ form_end(filterForm) }}
- \ No newline at end of file + From d3450fe63767a36685ef687ae77c3efbe01e07f2 Mon Sep 17 00:00:00 2001 From: Fabian Wunsch Date: Thu, 5 Sep 2024 16:01:27 +0200 Subject: [PATCH 2/3] fixed typos --- migrations/Version20240905085300.php | 6 +++--- src/Services/LabelSystem/SandboxedTwigFactory.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/migrations/Version20240905085300.php b/migrations/Version20240905085300.php index 4c3d45718..834444ae7 100644 --- a/migrations/Version20240905085300.php +++ b/migrations/Version20240905085300.php @@ -16,18 +16,18 @@ public function getDescription(): string public function mySQLUp(Schema $schema): void { - $this->addSql('ALTER TABLE parts ADD orderamount DOUBLE PRECISION NOT NULL DEFAULT 0, ADD orderDate DATETIME'); + $this->addSql('ALTER TABLE parts ADD orderamount DOUBLE PRECISION NOT NULL DEFAULT 0, ADD orderDelivery DATETIME'); } public function mySQLDown(Schema $schema): void { - $this->addSql('ALTER TABLE `parts` DROP orderamount, DROP orderDate'); + $this->addSql('ALTER TABLE `parts` DROP orderamount, DROP orderDelivery'); } public function sqLiteUp(Schema $schema): void { $this->addSql('ALTER TABLE parts ADD COLUMN orderamount DOUBLE PRECISION NOT NULL DEFAULT 0'); - $this->addSql('ALTER TABLE parts ADD COLUMN orderDate DATETIME'); + $this->addSql('ALTER TABLE parts ADD COLUMN orderDelivery DATETIME'); } public function sqLiteDown(Schema $schema): void diff --git a/src/Services/LabelSystem/SandboxedTwigFactory.php b/src/Services/LabelSystem/SandboxedTwigFactory.php index 5a97938e8..4b9423564 100644 --- a/src/Services/LabelSystem/SandboxedTwigFactory.php +++ b/src/Services/LabelSystem/SandboxedTwigFactory.php @@ -133,7 +133,7 @@ final class SandboxedTwigFactory Supplier::class => ['getShippingCosts', 'getDefaultCurrency'], Part::class => ['isNeedsReview', 'getTags', 'getMass', 'getIpn', 'getProviderReference', 'getDescription', 'getComment', 'isFavorite', 'getCategory', 'getFootprint', - 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getOrderAmount', 'getOrderDate', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum', + 'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getOrderAmount', 'getOrderDelivery', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum', 'getManufacturerProductUrl', 'getCustomProductURL', 'getManufacturingStatus', 'getManufacturer', 'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete', 'getParameters', 'getGroupedParameters', From 69bf9f6cedd20b51f353f38a14bb431f9478460f Mon Sep 17 00:00:00 2001 From: Fabian Wunsch Date: Thu, 5 Sep 2024 16:03:34 +0200 Subject: [PATCH 3/3] Fixed typos --- templates/parts/edit/_main.html.twig | 2 +- templates/parts/info/_main_infos.html.twig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/parts/edit/_main.html.twig b/templates/parts/edit/_main.html.twig index 22a0c5647..8fcdfe862 100644 --- a/templates/parts/edit/_main.html.twig +++ b/templates/parts/edit/_main.html.twig @@ -11,7 +11,7 @@ {{ form_row(form.tags) }} {{ form_row(form.minAmount) }} {{ form_row(form.orderAmount) }} -{{ form_row(form.orderDate) }} +{{ form_row(form.orderDelivery) }} {{ form_row(form.footprint) }} diff --git a/templates/parts/info/_main_infos.html.twig b/templates/parts/info/_main_infos.html.twig index 86f2d06e5..df07e401e 100644 --- a/templates/parts/info/_main_infos.html.twig +++ b/templates/parts/info/_main_infos.html.twig @@ -58,10 +58,10 @@ {% if part.orderAmount > 0 %} (+ {{ part.orderAmount | format_amount(part.partUnit) }} - {% if part.orderDate %} + {% if part.orderDelivery %} @ - - {{ part.orderDate | format_date() }}
+ + {{ part.orderDelivery | format_date() }}
{% endif %} )