Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement order details #691

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions migrations/Version20240905085300.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use App\Migration\AbstractMultiPlatformMigration;
use Doctrine\DBAL\Schema\Schema;

final class Version20240905085300 extends AbstractMultiPlatformMigration
{
public function getDescription(): string
{
return 'Added order fields';
}

public function mySQLUp(Schema $schema): void
{
$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 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 orderDelivery DATETIME');
}

public function sqLiteDown(Schema $schema): void
{
$error;
// TODO: implement backwards migration for SQlite
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}
}
4 changes: 4 additions & 0 deletions src/DataTables/Filters/PartFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions src/DataTables/PartsDataTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Expand Down
34 changes: 32 additions & 2 deletions src/Entity/Parts/Part.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
{
Expand Down
40 changes: 39 additions & 1 deletion src/Entity/Parts/PartTraits/InstockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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()).
Expand All @@ -158,7 +181,7 @@ public function useFloatAmount(): bool
*/
public function isNotEnoughInstock(): bool
{
return $this->getAmountSum() < $this->getMinAmount();
return ($this->getAmountSum() + $this->getOrderAmount()) < $this->getMinAmount();
}

/**
Expand Down Expand Up @@ -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;
}
}
10 changes: 10 additions & 0 deletions src/Form/Filters/PartFilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions src/Form/Part/PartBaseType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'),
Expand Down
3 changes: 3 additions & 0 deletions src/Serializer/PartNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/Services/LabelSystem/SandboxedTwigFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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', 'getOrderDelivery', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum',
'getManufacturerProductUrl', 'getCustomProductURL', 'getManufacturingStatus', 'getManufacturer',
'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete',
'getParameters', 'getGroupedParameters',
Expand Down
2 changes: 2 additions & 0 deletions templates/parts/edit/_main.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
{{ form_row(form.category) }}
{{ form_row(form.tags) }}
{{ form_row(form.minAmount) }}
{{ form_row(form.orderAmount) }}
{{ form_row(form.orderDelivery) }}

{{ form_row(form.footprint) }}

13 changes: 12 additions & 1 deletion templates/parts/info/_main_infos.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@
{% if part.expiredAmountSum > 0 %}
<span title="{% trans %}part_lots.is_expired{% endtrans %}" class="text-muted">(+{{ part.expiredAmountSum }})</span>
{% endif %}
{% if part.orderAmount > 0 %}
(+
<span title="{% trans %}orderstock.label{% endtrans %}">{{ part.orderAmount | format_amount(part.partUnit) }}</span>
{% if part.orderDelivery %}
@
<span class="badge bg-info mb-1" title="{% trans %}part.filter.orderDelivery{% endtrans %}">
<i class="fas fa-calendar-alt fa-fw"></i> {{ part.orderDelivery | format_date() }}<br>
</span>
{% endif %}
)
{% endif %}
/
<span title="{% trans %}mininstock.label{% endtrans %}">{{ part.minAmount | format_amount(part.partUnit) }}</span>
</span>
Expand Down Expand Up @@ -93,4 +104,4 @@
</h6>
{% endif %} #}
</div>
</div>
</div>
4 changes: 3 additions & 1 deletion templates/parts/lists/_filter.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
<div class="tab-pane pt-3" id="filter-stocks" role="tabpanel" aria-labelledby="filter-stocks-tab" tabindex="0">
{{ 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) }}
Expand Down Expand Up @@ -150,4 +152,4 @@
{{ form_end(filterForm) }}
</div>
</div>
</div>
</div>