diff --git a/src/Entity/LogSystem/AbstractLogEntry.php b/src/Entity/LogSystem/AbstractLogEntry.php index 2fafd8a1..e2dca513 100644 --- a/src/Entity/LogSystem/AbstractLogEntry.php +++ b/src/Entity/LogSystem/AbstractLogEntry.php @@ -67,10 +67,11 @@ use Psr\Log\LogLevel; * 6 = "ElementCreatedLogEntry", * 7 = "ElementEditedLogEntry", * 8 = "ConfigChangedLogEntry", - * 9 = "InstockChangedLogEntry", + * 9 = "LegacyInstockChangedLogEntry", * 10 = "DatabaseUpdatedLogEntry", * 11 = "CollectionElementDeleted", * 12 = "SecurityEventLogEntry", + * 13 = "PartStockChangedLogEntry", * }) */ abstract class AbstractLogEntry extends AbstractDBElement diff --git a/src/Entity/LogSystem/InstockChangedLogEntry.php b/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php similarity index 97% rename from src/Entity/LogSystem/InstockChangedLogEntry.php rename to src/Entity/LogSystem/LegacyInstockChangedLogEntry.php index c4de7469..35d58592 100644 --- a/src/Entity/LogSystem/InstockChangedLogEntry.php +++ b/src/Entity/LogSystem/LegacyInstockChangedLogEntry.php @@ -27,7 +27,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity() */ -class InstockChangedLogEntry extends AbstractLogEntry +class LegacyInstockChangedLogEntry extends AbstractLogEntry { protected string $typeString = 'instock_changed'; diff --git a/src/Entity/LogSystem/PartStockChangedLogEntry.php b/src/Entity/LogSystem/PartStockChangedLogEntry.php new file mode 100644 index 00000000..44852076 --- /dev/null +++ b/src/Entity/LogSystem/PartStockChangedLogEntry.php @@ -0,0 +1,224 @@ +. + */ + +namespace App\Entity\LogSystem; + +use App\Entity\Parts\PartLot; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity() + */ +class PartStockChangedLogEntry extends AbstractLogEntry +{ + public const TYPE_ADD = "add"; + public const TYPE_WITHDRAW = "withdraw"; + public const TYPE_MOVE = "move"; + + protected string $typeString = 'part_stock_changed'; + + protected const COMMENT_MAX_LENGTH = 300; + + /** + * Creates a new part stock changed log entry. + * @param string $type The type of the log entry. One of the TYPE_* constants. + * @param PartLot $lot The part lot which has been changed. + * @param float $old_stock The old stock of the lot. + * @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 PartLot|null $move_to_target The target lot if the type is TYPE_MOVE. + */ + protected function __construct(string $type, PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, ?PartLot $move_to_target = null) + { + parent::__construct(); + + if (!in_array($type, [self::TYPE_ADD, self::TYPE_WITHDRAW, self::TYPE_MOVE], true)) { + throw new \InvalidArgumentException('Invalid type for PartStockChangedLogEntry!'); + } + + //Same as every other element change log entry + $this->level = self::LEVEL_INFO; + + $this->setTargetElement($lot); + + $this->typeString = 'part_stock_changed'; + $this->extra = array_merge($this->extra, [ + 't' => $this->typeToShortType($type), + 'o' => $old_stock, + 'n' => $new_stock, + 'p' => $new_total_part_instock, + ]); + if (!empty($comment)) { + $this->extra['c'] = mb_strimwidth($comment, 0, self::COMMENT_MAX_LENGTH, '...'); + } + + if ($move_to_target) { + if ($type !== self::TYPE_MOVE) { + throw new \InvalidArgumentException('The move_to_target parameter can only be set if the type is "move"!'); + } + + $this->extra['m'] = $move_to_target->getID(); + } + } + + /** + * Creates a new log entry for adding stock to a lot. + * @param PartLot $lot The part lot which has been changed. + * @param float $old_stock The old stock of the lot. + * @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. + * @return static + */ + public static function add(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + { + return new self(self::TYPE_ADD, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + } + + /** + * Creates a new log entry for withdrawing stock from a lot. + * @param PartLot $lot The part lot which has been changed. + * @param float $old_stock The old stock of the lot. + * @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. + * @return static + */ + public static function withdraw(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment): self + { + return new self(self::TYPE_WITHDRAW, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment); + } + + /** + * Creates a new log entry for moving stock from a lot to another lot. + * @param PartLot $lot The part lot which has been changed. + * @param float $old_stock The old stock of the lot. + * @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 PartLot $move_to_target The target lot. + */ + public static function move(PartLot $lot, float $old_stock, float $new_stock, float $new_total_part_instock, string $comment, PartLot $move_to_target): self + { + return new self(self::TYPE_MOVE, $lot, $old_stock, $new_stock, $new_total_part_instock, $comment, $move_to_target); + } + + /** + * Returns the instock change type of this entry + * @return string One of the TYPE_* constants. + */ + public function getInstockChangeType(): string + { + return $this->shortTypeToType($this->extra['t']); + } + + /** + * Returns the old stock of the lot. + * @return float + */ + public function getOldStock(): float + { + return $this->extra['o']; + } + + /** + * Returns the new stock of the lot. + * @return float + */ + public function getNewStock(): float + { + return $this->extra['n']; + } + + /** + * Returns the new total instock of the part. + * @return float + */ + public function getNewTotalPartInstock(): float + { + return $this->extra['p']; + } + + /** + * Returns the comment associated with the change. + * @return string + */ + public function getComment(): string + { + return $this->extra['c'] ?? ''; + } + + /** + * Gets the difference between the old and the new stock value of the lot as a positive number. + * @return float + */ + public function getChangeAmount(): float + { + return abs($this->getNewStock() - $this->getOldStock()); + } + + /** + * Returns the target lot ID (where the instock was moved to) if the type is TYPE_MOVE. + * @return int|null + */ + public function getMoveToTargetID(): ?int + { + return $this->extra['m'] ?? null; + } + + /** + * Converts the human-readable type (TYPE_* consts) to the version stored in DB + * @param string $type + * @return string + */ + protected function typeToShortType(string $type): string + { + switch ($type) { + case self::TYPE_ADD: + return 'a'; + case self::TYPE_WITHDRAW: + return 'w'; + case self::TYPE_MOVE: + return 'm'; + default: + throw new \InvalidArgumentException('Invalid type: '.$type); + } + } + + /** + * Converts the short type stored in DB to the human-readable type (TYPE_* consts). + * @param string $short_type + * @return string + */ + protected function shortTypeToType(string $short_type): string + { + switch ($short_type) { + case 'a': + return self::TYPE_ADD; + case 'w': + return self::TYPE_WITHDRAW; + case 'm': + return self::TYPE_MOVE; + default: + throw new \InvalidArgumentException('Invalid short type: '.$short_type); + } + } +} \ No newline at end of file diff --git a/src/Form/Filters/LogFilterType.php b/src/Form/Filters/LogFilterType.php index e9cb7a53..ec004f9c 100644 --- a/src/Form/Filters/LogFilterType.php +++ b/src/Form/Filters/LogFilterType.php @@ -32,7 +32,7 @@ use App\Entity\LogSystem\DatabaseUpdatedLogEntry; use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; -use App\Entity\LogSystem\InstockChangedLogEntry; +use App\Entity\LogSystem\LegacyInstockChangedLogEntry; use App\Entity\LogSystem\SecurityEventLogEntry; use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\LogSystem\UserLogoutLogEntry; @@ -88,7 +88,7 @@ class LogFilterType extends AbstractType 'log.type.user_not_allowed' => UserNotAllowedLogEntry::class, //Legacy entries - 'log.type.instock_changed' => InstockChangedLogEntry::class, + 'log.type.instock_changed' => LegacyInstockChangedLogEntry::class, ]; public function configureOptions(OptionsResolver $resolver): void diff --git a/src/Services/LogSystem/LogEntryExtraFormatter.php b/src/Services/LogSystem/LogEntryExtraFormatter.php index 4b19c103..e8508707 100644 --- a/src/Services/LogSystem/LogEntryExtraFormatter.php +++ b/src/Services/LogSystem/LogEntryExtraFormatter.php @@ -31,7 +31,7 @@ use App\Entity\LogSystem\ElementCreatedLogEntry; use App\Entity\LogSystem\ElementDeletedLogEntry; use App\Entity\LogSystem\ElementEditedLogEntry; use App\Entity\LogSystem\ExceptionLogEntry; -use App\Entity\LogSystem\InstockChangedLogEntry; +use App\Entity\LogSystem\LegacyInstockChangedLogEntry; use App\Entity\LogSystem\SecurityEventLogEntry; use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\LogSystem\UserLogoutLogEntry; @@ -155,7 +155,7 @@ class LogEntryExtraFormatter $array['log.element_edited.changed_fields'] = htmlspecialchars(implode(', ', $context->getChangedFields())); } - if ($context instanceof InstockChangedLogEntry) { + if ($context instanceof LegacyInstockChangedLogEntry) { $array[] = $this->translator->trans($context->isWithdrawal() ? 'log.instock_changed.withdrawal' : 'log.instock_changed.added'); $array[] = sprintf( '%s %s (%s)', diff --git a/src/Services/Parts/PartLotWithdrawAddHelper.php b/src/Services/Parts/PartLotWithdrawAddHelper.php index a757398d..a6a9ef93 100644 --- a/src/Services/Parts/PartLotWithdrawAddHelper.php +++ b/src/Services/Parts/PartLotWithdrawAddHelper.php @@ -2,11 +2,20 @@ namespace App\Services\Parts; +use App\Entity\LogSystem\PartStockChangedLogEntry; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; +use App\Services\LogSystem\EventLogger; -class PartLotWithdrawAddHelper +final class PartLotWithdrawAddHelper { + private $eventLogger; + + public function __construct(EventLogger $eventLogger) + { + $this->eventLogger = $eventLogger; + } + /** * Checks whether the given part can * @param PartLot $partLot @@ -80,7 +89,11 @@ class PartLotWithdrawAddHelper } //Subtract the amount from the part lot - $partLot->setAmount($partLot->getAmount() - $amount); + $oldAmount = $partLot->getAmount(); + $partLot->setAmount($oldAmount - $amount); + + $event = PartStockChangedLogEntry::withdraw($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $this->eventLogger->log($event); return $partLot; } @@ -111,8 +124,11 @@ class PartLotWithdrawAddHelper throw new \RuntimeException("Cannot add to this part lot!"); } - //Subtract the amount from the part lot - $partLot->setAmount($partLot->getAmount() + $amount); + $oldAmount = $partLot->getAmount(); + $partLot->setAmount($oldAmount + $amount); + + $event = PartStockChangedLogEntry::add($partLot, $oldAmount, $partLot->getAmount(), $part->getAmountSum() , $comment); + $this->eventLogger->log($event); return $partLot; } @@ -154,9 +170,14 @@ class PartLotWithdrawAddHelper throw new \RuntimeException('Not enough stock to withdraw!'); } + $oldOriginAmount = $origin->getAmount(); + //Subtract the amount from the part lot $origin->setAmount($origin->getAmount() - $amount); //And add it to the target $target->setAmount($target->getAmount() + $amount); + + $event = PartStockChangedLogEntry::move($origin, $oldOriginAmount, $origin->getAmount(), $part->getAmountSum() , $comment, $target); + $this->eventLogger->log($event); } } \ No newline at end of file