diff --git a/src/Controller/PartController.php b/src/Controller/PartController.php index 53fc01eb..301557b7 100644 --- a/src/Controller/PartController.php +++ b/src/Controller/PartController.php @@ -401,22 +401,33 @@ class PartController extends AbstractController $comment = $request->request->get('comment'); $action = $request->request->get('action'); + $timestamp = null; + $timestamp_str = $request->request->getString('timestamp', ''); + //Try to parse the timestamp + if($timestamp_str !== '') { + $timestamp = new DateTime($timestamp_str); + } + + //Ensure that the timestamp is not in the future + if($timestamp !== null && $timestamp > new DateTime("+20min")) { + throw new \LogicException("The timestamp must not be in the future!"); + } try { switch ($action) { case "withdraw": case "remove": $this->denyAccessUnlessGranted('withdraw', $partLot); - $withdrawAddHelper->withdraw($partLot, $amount, $comment); + $withdrawAddHelper->withdraw($partLot, $amount, $comment, $timestamp); break; case "add": $this->denyAccessUnlessGranted('add', $partLot); - $withdrawAddHelper->add($partLot, $amount, $comment); + $withdrawAddHelper->add($partLot, $amount, $comment, $timestamp); break; case "move": $this->denyAccessUnlessGranted('move', $partLot); $this->denyAccessUnlessGranted('move', $targetLot); - $withdrawAddHelper->move($partLot, $targetLot, $amount, $comment); + $withdrawAddHelper->move($partLot, $targetLot, $amount, $comment, $timestamp); break; default: throw new \RuntimeException("Unknown action!"); diff --git a/src/Entity/LogSystem/PartStockChangedLogEntry.php b/src/Entity/LogSystem/PartStockChangedLogEntry.php index 63288d0d..dd7b5234 100644 --- a/src/Entity/LogSystem/PartStockChangedLogEntry.php +++ b/src/Entity/LogSystem/PartStockChangedLogEntry.php @@ -41,8 +41,10 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. * @param PartLot|null $move_to_target The target lot if the type is TYPE_MOVE. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. */ - protected function __construct(PartStockChangeType $type, PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?PartLot $move_to_target = null) + protected function __construct(PartStockChangeType $type, PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?PartLot $move_to_target = null, + ?\DateTimeInterface $action_timestamp = null) { parent::__construct(); @@ -62,6 +64,11 @@ class PartStockChangedLogEntry extends AbstractLogEntry $this->extra['c'] = mb_strimwidth($comment, 0, self::COMMENT_MAX_LENGTH, '...'); } + if ($action_timestamp instanceof \DateTimeInterface) { + //The action timestamp is saved as an ISO 8601 string + $this->extra['a'] = $action_timestamp->format(\DateTimeInterface::ATOM); + } + if ($move_to_target instanceof PartLot) { if ($type !== PartStockChangeType::MOVE) { throw new \InvalidArgumentException('The move_to_target parameter can only be set if the type is "move"!'); @@ -78,11 +85,12 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_stock The new stock of the lot. * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. * @return self */ - public static function add(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + public static function add(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?\DateTimeInterface $action_timestamp = null): self { - return new self(PartStockChangeType::ADD, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + return new self(PartStockChangeType::ADD, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, action_timestamp: $action_timestamp); } /** @@ -92,11 +100,12 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_stock The new stock of the lot. * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. * @return self */ - public static function withdraw(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + public static function withdraw(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?\DateTimeInterface $action_timestamp = null): self { - return new self(PartStockChangeType::WITHDRAW, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + return new self(PartStockChangeType::WITHDRAW, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, action_timestamp: $action_timestamp); } /** @@ -107,10 +116,12 @@ class PartStockChangedLogEntry extends AbstractLogEntry * @param float $new_total_part_instock The new total instock of the part. * @param string $comment The comment associated with the change. * @param PartLot $move_to_target The target lot. + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. + * @return self */ - public static function move(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, PartLot $move_to_target): self + public static function move(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, PartLot $move_to_target, ?\DateTimeInterface $action_timestamp = null): self { - return new self(PartStockChangeType::MOVE, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, $move_to_target); + return new self(PartStockChangeType::MOVE, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, $move_to_target, action_timestamp: $action_timestamp); } /** @@ -169,4 +180,18 @@ class PartStockChangedLogEntry extends AbstractLogEntry { return $this->extra['m'] ?? null; } + + /** + * Returns the timestamp when this action was performed and not when the log entry was created. + * This is useful if the action happened in the past, and the log entry is created afterwards. + * If the timestamp is not set, null is returned. + * @return \DateTimeInterface|null + */ + public function getActionTimestamp(): ?\DateTimeInterface + { + if (!empty($this->extra['a'])) { + return \DateTimeImmutable::createFromFormat(\DateTimeInterface::ATOM, $this->extra['a']); + } + return null; + } } diff --git a/src/Services/LogSystem/LogEntryExtraFormatter.php b/src/Services/LogSystem/LogEntryExtraFormatter.php index a1fd4c9b..fdfd72c8 100644 --- a/src/Services/LogSystem/LogEntryExtraFormatter.php +++ b/src/Services/LogSystem/LogEntryExtraFormatter.php @@ -193,6 +193,10 @@ class LogEntryExtraFormatter htmlspecialchars($this->elementTypeNameGenerator->getLocalizedTypeLabel(PartLot::class)) .' ' . $context->getMoveToTargetID(); } + if ($context->getActionTimestamp()) { + $formatter = new \IntlDateFormatter($this->translator->getLocale(), \IntlDateFormatter::SHORT, \IntlDateFormatter::SHORT); + $array['log.part_stock_changed.timestamp'] = $formatter->format($context->getActionTimestamp()); + } } return $array; diff --git a/src/Services/Parts/PartLotWithdrawAddHelper.php b/src/Services/Parts/PartLotWithdrawAddHelper.php index 8086081f..a838beea 100644 --- a/src/Services/Parts/PartLotWithdrawAddHelper.php +++ b/src/Services/Parts/PartLotWithdrawAddHelper.php @@ -53,9 +53,10 @@ final class PartLotWithdrawAddHelper * @param PartLot $partLot The partLot from which the instock should be taken (which value should be decreased) * @param float $amount The amount of parts that should be taken from the part lot * @param string|null $comment The optional comment describing the reason for the withdrawal + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. * @return PartLot The modified part lot */ - public function withdraw(PartLot $partLot, float $amount, ?string $comment = null): PartLot + public function withdraw(PartLot $partLot, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null): PartLot { //Ensure that amount is positive if ($amount <= 0) { @@ -83,7 +84,7 @@ final class PartLotWithdrawAddHelper $oldAmount = $partLot->getAmount(); $partLot->setAmount($oldAmount - $amount); - $event = PartStockChangedLogEntry::withdraw($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $event = PartStockChangedLogEntry::withdraw($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry @@ -100,9 +101,10 @@ final class PartLotWithdrawAddHelper * @param PartLot $partLot The partLot from which the instock should be taken (which value should be decreased) * @param float $amount The amount of parts that should be taken from the part lot * @param string|null $comment The optional comment describing the reason for the withdrawal + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. * @return PartLot The modified part lot */ - public function add(PartLot $partLot, float $amount, ?string $comment = null): PartLot + public function add(PartLot $partLot, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null): PartLot { if ($amount <= 0) { throw new \InvalidArgumentException('Amount must be positive'); @@ -123,7 +125,7 @@ final class PartLotWithdrawAddHelper $oldAmount = $partLot->getAmount(); $partLot->setAmount($oldAmount + $amount); - $event = PartStockChangedLogEntry::add($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $event = PartStockChangedLogEntry::add($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry @@ -141,8 +143,9 @@ final class PartLotWithdrawAddHelper * @param PartLot $target The part lot to which the parts should be added * @param float $amount The amount of parts that should be moved * @param string|null $comment A comment describing the reason for the move + * @param \DateTimeInterface|null $action_timestamp The optional timestamp, where the action happened. Useful if the action happened in the past, and the log entry is created afterwards. */ - public function move(PartLot $origin, PartLot $target, float $amount, ?string $comment = null): void + public function move(PartLot $origin, PartLot $target, float $amount, ?string $comment = null, ?\DateTimeInterface $action_timestamp = null): void { if ($amount <= 0) { throw new \InvalidArgumentException('Amount must be positive'); @@ -177,7 +180,7 @@ final class PartLotWithdrawAddHelper //And add it to the target $target->setAmount($target->getAmount() + $amount); - $event = PartStockChangedLogEntry::move($origin, $oldOriginAmount, $origin->getAmount(), $part->getAmountSum() , $comment, $target); + $event = PartStockChangedLogEntry::move($origin, $oldOriginAmount, $origin->getAmount(), $part->getAmountSum() , $comment, $target, $action_timestamp); $this->eventLogger->log($event); //Apply the comment also to global events, so it gets associated with the elementChanged log entry diff --git a/templates/parts/info/_withdraw_modal.html.twig b/templates/parts/info/_withdraw_modal.html.twig index 75b802fd..b5c4308f 100644 --- a/templates/parts/info/_withdraw_modal.html.twig +++ b/templates/parts/info/_withdraw_modal.html.twig @@ -50,6 +50,18 @@