diff --git a/application/Espo/Classes/ConsoleCommands/PopulateNumbers.php b/application/Espo/Classes/ConsoleCommands/PopulateNumbers.php index 7d2eeadce8..3a168e5a74 100644 --- a/application/Espo/Classes/ConsoleCommands/PopulateNumbers.php +++ b/application/Espo/Classes/ConsoleCommands/PopulateNumbers.php @@ -35,7 +35,7 @@ use Espo\Core\Console\Exceptions\ArgumentNotSpecified; use Espo\Core\Console\Exceptions\InvalidArgument; use Espo\Core\Console\IO; use Espo\Core\Exceptions\Error; -use Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor; +use Espo\Core\FieldProcessing\NextNumber\Processor; use Espo\Core\Name\Field; use Espo\Core\ORM\Entity as CoreEntity; use Espo\Core\ORM\Repository\Option\SaveOption; @@ -44,11 +44,11 @@ use Espo\ORM\Query\Part\Order; class PopulateNumbers implements Command { - private BeforeSaveProcessor $beforeSaveProcessor; + private Processor $beforeSaveProcessor; private EntityManager $entityManager; public function __construct( - BeforeSaveProcessor $beforeSaveProcessor, + Processor $beforeSaveProcessor, EntityManager $entityManager ) { $this->beforeSaveProcessor = $beforeSaveProcessor; diff --git a/application/Espo/Core/FieldProcessing/NextNumber/BeforeSaveProcessor.php b/application/Espo/Core/FieldProcessing/NextNumber/Processor.php similarity index 84% rename from application/Espo/Core/FieldProcessing/NextNumber/BeforeSaveProcessor.php rename to application/Espo/Core/FieldProcessing/NextNumber/Processor.php index 2f619580e9..73bc05d970 100644 --- a/application/Espo/Core/FieldProcessing/NextNumber/BeforeSaveProcessor.php +++ b/application/Espo/Core/FieldProcessing/NextNumber/Processor.php @@ -32,15 +32,19 @@ namespace Espo\Core\FieldProcessing\NextNumber; use Espo\Core\Exceptions\Error; use Espo\Core\ORM\Repository\Option\SaveOption; use Espo\Core\ORM\Type\FieldType; -use Espo\Entities\NextNumber; - use Espo\Core\ORM\Entity; use Espo\Core\ORM\EntityManager; use Espo\Core\Utils\Metadata; +use Espo\Entities\NextNumber; +use Espo\ORM\Repository\Option\SaveOptions; +use RuntimeException; use const STR_PAD_LEFT; -class BeforeSaveProcessor +/** + * @since 9.3.0 + */ +class Processor { /** @var array */ private $fieldListMapCache = []; @@ -52,6 +56,7 @@ class BeforeSaveProcessor /** * For an existing record. + * * @throws Error */ public function processPopulate(Entity $entity, string $field): void @@ -65,18 +70,37 @@ class BeforeSaveProcessor $this->processItem($entity, $field, [], true); } - /** - * @param array $options - */ - public function process(Entity $entity, array $options): void + public function process(Entity $entity, SaveOptions $options): void { $fieldList = $this->getFieldList($entity->getEntityType()); foreach ($fieldList as $field) { - $this->processItem($entity, $field, $options); + $this->processItem($entity, $field, $options->toAssoc()); } } + /** + * To be invoked in custom hooks. The default hook must be suppressed. + * Useful when the number should change only if some conditions met. + * + * @since 9.3.0 + * @noinspection PhpUnused + */ + public function processField(Entity $entity, string $field, SaveOptions $options): void + { + if ( + $this->entityManager + ->getDefs() + ->getEntity($entity->getEntityType()) + ->getField($field) + ->getType() !== FieldType::NUMBER + ) { + throw new RuntimeException("Not a 'number' field."); + } + + $this->processItem($entity, $field, $options->toAssoc()); + } + /** * @param array $options */ @@ -141,6 +165,10 @@ class BeforeSaveProcessor continue; } + if ($defs->getParam('suppressHook')) { + continue; + } + $list[] = $name; } diff --git a/application/Espo/Hooks/Common/NextNumber.php b/application/Espo/Hooks/Common/NextNumber.php index d8f2f38a97..797f27c2d2 100644 --- a/application/Espo/Hooks/Common/NextNumber.php +++ b/application/Espo/Hooks/Common/NextNumber.php @@ -32,7 +32,7 @@ namespace Espo\Hooks\Common; use Espo\Core\Hook\Hook\BeforeSave; use Espo\Core\ORM\Entity as CoreEntity; use Espo\ORM\Entity; -use Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor as Processor; +use Espo\Core\FieldProcessing\NextNumber\Processor as Processor; use Espo\ORM\Repository\Option\SaveOptions; /** @@ -45,6 +45,6 @@ class NextNumber implements BeforeSave public function beforeSave(Entity $entity, SaveOptions $options): void { - $this->processor->process($entity, $options->toAssoc()); + $this->processor->process($entity, $options); } } diff --git a/application/Espo/Resources/metadata/fields/number.json b/application/Espo/Resources/metadata/fields/number.json index 3e2551d44c..a0b7496aff 100644 --- a/application/Espo/Resources/metadata/fields/number.json +++ b/application/Espo/Resources/metadata/fields/number.json @@ -25,6 +25,11 @@ "name": "copyToClipboard", "type": "bool", "default": false + }, + { + "name": "suppressHook", + "type": "bool", + "default": false } ], "filter": true, diff --git a/schema/metadata/entityDefs.json b/schema/metadata/entityDefs.json index f6dac9a47f..d15103872d 100644 --- a/schema/metadata/entityDefs.json +++ b/schema/metadata/entityDefs.json @@ -552,6 +552,33 @@ } } }, + { + "if": { + "properties": { + "type": { + "anyOf": [ + {"const": "number"} + ] + } + } + }, + "then": { + "properties": { + "prefix": { + "type": "string", + "description": "Prefix." + }, + "padLength": { + "type": "integer", + "description": "Pad length." + }, + "suppressHook": { + "type": "boolean", + "description": "Suppress the default hook. Useful when a custom hook needs to be used. As of v9.3.0." + } + } + } + }, { "if": { "properties": {