diff --git a/application/Espo/Core/FieldValidators/CurrencyType.php b/application/Espo/Core/FieldValidators/CurrencyType.php index e86ace1590..09c7475b79 100644 --- a/application/Espo/Core/FieldValidators/CurrencyType.php +++ b/application/Espo/Core/FieldValidators/CurrencyType.php @@ -31,4 +31,11 @@ namespace Espo\Core\FieldValidators; class CurrencyType extends FloatType { + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return + $entity->has($field) && $entity->get($field) !== null && + $entity->has($field . 'Currency') && $entity->get($field . 'Currency') !== null && + $entity->get($field . 'Currency') !== ''; + } } diff --git a/application/Espo/Core/FieldValidators/DateType.php b/application/Espo/Core/FieldValidators/DateType.php index 211c421e87..2a8379c950 100644 --- a/application/Espo/Core/FieldValidators/DateType.php +++ b/application/Espo/Core/FieldValidators/DateType.php @@ -32,6 +32,11 @@ namespace Espo\Core\FieldValidators; class DateType extends BaseType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool + { + return $this->isNotEmpty($entity, $field); + } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) { return $entity->has($field) && $entity->get($field) !== null; } diff --git a/application/Espo/Core/FieldValidators/DatetimeOptionalType.php b/application/Espo/Core/FieldValidators/DatetimeOptionalType.php index 7841bea02e..a9e5515985 100644 --- a/application/Espo/Core/FieldValidators/DatetimeOptionalType.php +++ b/application/Espo/Core/FieldValidators/DatetimeOptionalType.php @@ -32,6 +32,11 @@ namespace Espo\Core\FieldValidators; class DatetimeOptionalType extends DatetimeType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool + { + return $this->isNotEmpty($entity, $field); + } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) { if ($entity->has($field) && $entity->get($field) !== null) return true; if ($entity->has($field . 'Date') && $entity->get($field . 'Date') !== null) return true; diff --git a/application/Espo/Core/FieldValidators/EmailType.php b/application/Espo/Core/FieldValidators/EmailType.php index f9309435f1..04249931e2 100644 --- a/application/Espo/Core/FieldValidators/EmailType.php +++ b/application/Espo/Core/FieldValidators/EmailType.php @@ -33,7 +33,7 @@ class EmailType extends BaseType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if ($entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null) return true; + if ($this->isNotEmpty($entity, $field)) return true; $dataList = $entity->get($field . 'Data'); if (!is_array($dataList)) return false; @@ -47,7 +47,7 @@ class EmailType extends BaseType public function checkEmailAddress(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if ($entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null) { + if ($this->isNotEmpty($entity, $field)) { $address = $entity->get($field); if (!filter_var($address, FILTER_VALIDATE_EMAIL)) { return false; @@ -67,4 +67,9 @@ class EmailType extends BaseType return true; } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null; + } } diff --git a/application/Espo/Core/FieldValidators/EnumType.php b/application/Espo/Core/FieldValidators/EnumType.php new file mode 100644 index 0000000000..c7c06b56a1 --- /dev/null +++ b/application/Espo/Core/FieldValidators/EnumType.php @@ -0,0 +1,43 @@ +isNotEmpty($entity, $field); + } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return $entity->has($field) && $entity->get($field) !== null; + } +} diff --git a/application/Espo/Core/FieldValidators/IntType.php b/application/Espo/Core/FieldValidators/IntType.php index 683fabd02c..0a9a0b3fd8 100644 --- a/application/Espo/Core/FieldValidators/IntType.php +++ b/application/Espo/Core/FieldValidators/IntType.php @@ -33,20 +33,25 @@ class IntType extends BaseType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - return $entity->has($field) && $entity->get($field) !== null; + return $this->isNotEmpty($entity, $field); } public function checkMax(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if (!$entity->has($field) || $entity->get($field) === null) return true; + if (!$this->isNotEmpty($entity, $field)) return true; if ($entity->get($field) > $validationValue) return false; return true; } public function checkMin(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if (!$entity->has($field) || $entity->get($field) === null) return true; + if (!$this->isNotEmpty($entity, $field)) return true; if ($entity->get($field) < $validationValue) return false; return true; } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return $entity->has($field) && $entity->get($field) !== null; + } } diff --git a/application/Espo/Core/FieldValidators/PhoneType.php b/application/Espo/Core/FieldValidators/PhoneType.php index ed895bd885..c3c8897659 100644 --- a/application/Espo/Core/FieldValidators/PhoneType.php +++ b/application/Espo/Core/FieldValidators/PhoneType.php @@ -33,7 +33,7 @@ class PhoneType extends BaseType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if ($entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null) return true; + if ($this->isNotEmpty($entity, $field)) return true; $dataList = $entity->get($field . 'Data'); if (!is_array($dataList)) return false; @@ -44,4 +44,9 @@ class PhoneType extends BaseType return false; } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null; + } } diff --git a/application/Espo/Core/FieldValidators/VarcharType.php b/application/Espo/Core/FieldValidators/VarcharType.php index 80281a3c15..2ca0247596 100644 --- a/application/Espo/Core/FieldValidators/VarcharType.php +++ b/application/Espo/Core/FieldValidators/VarcharType.php @@ -33,12 +33,12 @@ class VarcharType extends BaseType { public function checkRequired(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null; + return $this->isNotEmpty($entity, $field); } public function checkMaxLength(\Espo\ORM\Entity $entity, string $field, $validationValue, $data) : bool { - if ($entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null) { + if ($this->isNotEmpty($entity, $field)) { $value = $entity->get($field); if (mb_strlen($value) > $validationValue) { return false; @@ -46,4 +46,9 @@ class VarcharType extends BaseType } return true; } + + protected function isNotEmpty(\Espo\ORM\Entity $entity, $field) + { + return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null; + } } diff --git a/application/Espo/Resources/metadata/fields/enum.json b/application/Espo/Resources/metadata/fields/enum.json index b922dc404d..e01e5fe357 100644 --- a/application/Espo/Resources/metadata/fields/enum.json +++ b/application/Espo/Resources/metadata/fields/enum.json @@ -42,6 +42,9 @@ "type":"bool" } ], + "validationList": [ + "required" + ], "filter": true, "fieldDefs":{ "type":"varchar" diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index 9b0646c47a..fd11cb73e8 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -929,6 +929,8 @@ class Record extends \Espo\Core\Services\Base $entity->set($data); + $this->processValidation($entity, $data); + $this->beforeUpdateEntity($entity, $data); if (!$this->checkAssignment($entity)) { @@ -1587,6 +1589,11 @@ class Record extends \Espo\Core\Services\Base $entity = $this->getEntity($id); if ($this->getAcl()->check($entity, 'edit') && $this->checkEntityForMassUpdate($entity, $data)) { $entity->set($data); + try { + $this->processValidation($entity, $data); + } catch (\Exception $e) { + continue; + } if ($this->checkAssignment($entity)) { if ($repository->save($entity, ['massUpdate' => true])) { $idsUpdated[] = $entity->id; @@ -1638,14 +1645,14 @@ class Record extends \Espo\Core\Services\Base $this->afterMassUpdate($idsUpdated, $data); - return [ + return (object) [ 'count' => $count ]; } $this->afterMassUpdate($idsUpdated, $data); - return [ + return (object) [ 'count' => $count, 'ids' => $idsUpdated ]; diff --git a/tests/integration/Espo/Record/FieldValidationTest.php b/tests/integration/Espo/Record/FieldValidationTest.php new file mode 100644 index 0000000000..791c74760e --- /dev/null +++ b/tests/integration/Espo/Record/FieldValidationTest.php @@ -0,0 +1,346 @@ +getContainer()->get('metadata'); + $metadata->set('entityDefs', $entityType, [ + 'fields' => $data + ]); + $metadata->save(); + } + + public function testRequiredVarchar1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => null + ]); + } + + public function testUpdateRequiredVarchar1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $entity = $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test' + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->update($entity->id, (object) [ + 'name' => '' + ]); + } + + public function testMassUpdateRequiredVarchar1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $entity = $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test' + ]); + + $result = $app->getContainer()->get('serviceFactory')->create('Account')->massUpdate((object) [ + 'name' => '' + ], [ + 'ids' => [$entity->id] + ]); + + $this->assertEquals(0, $result->count); + } + + public function testMassUpdateRequiredVarchar2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $entity = $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test' + ]); + + $result = $app->getContainer()->get('serviceFactory')->create('Account')->massUpdate((object) [ + 'name' => 'hello' + ], [ + 'ids' => [$entity->id] + ]); + + $this->assertEquals(1, $result->count); + } + + public function testRequiredVarchar2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + + ]); + } + + public function testRequiredVarchar3() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true + ] + ]); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test' + ]); + + $this->assertTrue(true); + } + + public function testMaxLength1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true, + 'maxLength' => 5 + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => '123456' + ]); + } + + public function testMaxLength2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'name' => [ + 'required' => true, + 'maxLength' => 5 + ] + ]); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => '12345' + ]); + + $this->assertTrue(true); + } + + public function testRequiredLink1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'assignedUser' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test', + 'assignedUserId' => null, + ]); + } + + public function testRequiredLink2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'assignedUser' => [ + 'required' => true + ] + ]); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test', + 'assignedUserId' => '1', + ]); + + $this->assertTrue(true); + } + + public function testRequiredLinkMultiple1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Account', [ + 'teams' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Account')->create((object) [ + 'name' => 'test', + 'teamsIds' => [], + ]); + } + + public function testRequiredCurrency1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'opportunityAmount' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test', + 'opportunityAmount' => null, + ]); + } + + public function testRequiredCurrency2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'opportunityAmount' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test', + 'opportunityAmount' => 100, + 'opportunityAmountCurrency' => null, + ]); + } + + public function testRequiredCurrency3() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'opportunityAmount' => [ + 'required' => true + ] + ]); + + $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test', + 'opportunityAmount' => 100, + 'opportunityAmountCurrency' => 'USD', + ]); + + $this->assertTrue(true); + } + + public function testRequiredEnum1() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'status' => [ + 'required' => true, + 'default' => null + ] + ]); + $app = $this->createApplication(); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $e = $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test' + ]); + + $this->assertTrue($status === null); + } + + public function testRequiredEnum2() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'status' => [ + 'required' => true + ] + ]); + + $this->expectException(\Espo\Core\Exceptions\BadRequest::class); + + $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test', + 'status' => null + ]); + } + + public function testRequiredEnum3() + { + $app = $this->createApplication(); + $this->setFieldsDefs($app, 'Lead', [ + 'status' => [ + 'required' => true + ] + ]); + + $e = $app->getContainer()->get('serviceFactory')->create('Lead')->create((object) [ + 'lastName' => 'test', + 'status' => 'New' + ]); + + $this->assertTrue(true); + } +}