Birth quick form (ported from farmOS 1.x) #656

This commit is contained in:
Michael Stenta 2022-02-25 08:15:25 -05:00
parent 3cd5de573b
commit 47ac9ae5dd
4 changed files with 805 additions and 0 deletions

View File

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Refresh map edit layer when WKT is pasted into data input field #670](https://github.com/farmOS/farmOS/pull/670)
- [Add QuickStringTrait::entityLabelsSummary() method for summarizing entity labels #675](https://github.com/farmOS/farmOS/pull/675)
- [Add asset inventory views field #679](https://github.com/farmOS/farmOS/pull/679)
- [Birth quick form (ported from farmOS 1.x) #656](https://github.com/farmOS/farmOS/pull/656)
### Changed

View File

@ -0,0 +1,11 @@
name: Birth Quick Form
description: Provides a quick form for recording births.
type: module
package: farmOS Quick Forms
core_version_requirement: ^9
dependencies:
- farm:farm_animal
- farm:farm_birth
- farm:farm_observation
- farm:farm_quantity_standard
- farm:farm_quick

View File

@ -0,0 +1,528 @@
<?php
namespace Drupal\farm_quick_birth\Plugin\QuickForm;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\farm_group\GroupMembershipInterface;
use Drupal\farm_location\AssetLocationInterface;
use Drupal\farm_quick\Plugin\QuickForm\QuickFormBase;
use Drupal\farm_quick\Traits\QuickAssetTrait;
use Drupal\farm_quick\Traits\QuickLogTrait;
use Drupal\farm_quick\Traits\QuickStringTrait;
use Psr\Container\ContainerInterface;
/**
* Birth quick form.
*
* @QuickForm(
* id = "birth",
* label = @Translation("Birth"),
* description = @Translation("Record an animal birth."),
* helpText = @Translation("Use this form to record the birth of one or more animals. A new birth log will be created, along with the new child animal asset records."),
* permissions = {
* "create animal asset",
* "create birth log",
* "create observation log",
* }
* )
*/
class Birth extends QuickFormBase {
use QuickAssetTrait;
use QuickLogTrait;
use QuickStringTrait;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Asset location service.
*
* @var \Drupal\farm_location\AssetLocationInterface
*/
protected $assetLocation;
/**
* Group membership service.
*
* @var \Drupal\farm_group\GroupMembershipInterface
*/
protected $groupMembership;
/**
* Current user object.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a QuickFormBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \Drupal\farm_location\AssetLocationInterface $asset_location
* Asset location service.
* @param \Drupal\farm_group\GroupMembershipInterface $group_membership
* Group membership service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user object.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MessengerInterface $messenger, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, AssetLocationInterface $asset_location, GroupMembershipInterface $group_membership, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $messenger);
$this->entityTypeManager = $entity_type_manager;
$this->moduleHandler = $module_handler;
$this->configFactory = $config_factory;
$this->assetLocation = $asset_location;
$this->groupMembership = $group_membership;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger'),
$container->get('entity_type.manager'),
$container->get('module_handler'),
$container->get('config.factory'),
$container->get('asset.location'),
$container->get('group.membership'),
$container->get('current_user'),
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Date of birth.
$form['date'] = [
'#type' => 'datetime',
'#title' => $this->t('Date of birth'),
'#default_value' => new DrupalDateTime('midnight', $this->currentUser->getTimeZone()),
'#required' => TRUE,
];
// Number of children.
$range = range(1, 15);
$form['child_count'] = [
'#type' => 'select',
'#title' => $this->t('How many children were born?'),
'#options' => array_combine($range, $range),
'#default_value' => 1,
'#ajax' => [
'callback' => [$this, 'childrenCallback'],
'wrapper' => 'children',
],
];
// Create a container for children.
$form['children'] = [
'#type' => 'container',
'#tree' => TRUE,
'#attributes' => ['id' => 'children'],
];
// Create a fieldset for each child.
$child_count = $form_state->getValue('child_count', 1);
for ($i = 0; $i < $child_count; $i++) {
$counter = ' ' . ($i + 1);
$form['children'][$i] = [
'#type' => 'details',
'#title' => $this->t('Child') . $counter,
'#open' => $i == 0,
];
// Child name.
$form['children'][$i]['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Name'),
'#description' => $this->t('Give the animal a name (and/or tag ID below). If the name is left blank, then it will be copied from the tag ID.'),
];
// Child ID tag.
$form['children'][$i]['tag'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['inline-container'],
],
];
$form['children'][$i]['tag']['type'] = [
'#type' => 'select',
'#title' => $this->t('Tag type'),
'#options' => [NULL => ''] + farm_id_tag_type_options('animal'),
];
$form['children'][$i]['tag']['id'] = [
'#type' => 'textfield',
'#title' => $this->t('Tag ID'),
'#size' => 16,
];
$form['children'][$i]['tag']['location'] = [
'#type' => 'textfield',
'#title' => $this->t('Tag location'),
'#size' => 16,
];
// Male or female.
$form['children'][$i]['sex'] = [
'#type' => 'radios',
'#title' => $this->t('Sex'),
'#options' => [
'F' => $this->t('Female'),
'M' => $this->t('Male'),
],
];
// Birth weight (metric: kg / us: lbs)
$units = $this->birthWeightUnits();
$form['children'][$i]['weight'] = [
'#type' => 'number',
'#title' => $this->t('Birth weight (@units)', ['@units' => $units]),
'#description' => $this->t('This will create a birth weight observation log associated with the child.'),
'#min' => 0,
'#step' => 0.01,
];
// Notes.
$form['children'][$i]['notes'] = [
'#type' => 'text_format',
'#title' => $this->t('Notes about this child'),
'#format' => 'default',
];
// Survived.
$form['children'][$i]['survived'] = [
'#type' => 'checkbox',
'#title' => $this->t('Survived birth'),
'#description' => $this->t('Uncheck this if the child did not survive. The child animal record will still be created, but will be immediately archived.'),
'#default_value' => TRUE,
];
}
// Create vertical tabs.
$form['tabs'] = [
'#type' => 'vertical_tabs',
];
// Create lineage tab.
$form['lineage'] = [
'#type' => 'details',
'#title' => $this->t('Lineage'),
'#group' => 'tabs',
];
// Birth mother.
$form['lineage']['birth_mother'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Birth mother'),
'#description' => $this->t('This is the mother giving birth. She will be referenced on the Birth log that is created.'),
'#target_type' => 'asset',
'#selection_settings' => [
'target_bundles' => ['animal'],
'sort' => [
'field' => 'status',
'direction' => 'ASC',
],
],
];
// Genetic mother.
$form['lineage']['genetic_mother'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Genetic mother'),
'#description' => $this->t("If the genetic mother is different from the birth mother, she can be referenced here for lineage tracking. Otherwise, it will be assumed that the birth mother is the genetic mother. This will be referenced as the child's parent."),
'#target_type' => 'asset',
'#selection_settings' => [
'target_bundles' => ['animal'],
'sort' => [
'field' => 'status',
'direction' => 'ASC',
],
],
];
// Genetic father.
$form['lineage']['genetic_father'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Genetic father'),
'#description' => $this->t("This will be referenced as the child's parent."),
'#target_type' => 'asset',
'#selection_settings' => [
'target_bundles' => ['animal'],
'sort' => [
'field' => 'status',
'direction' => 'ASC',
],
],
];
// If the group module is enabled, add an entity autocomplete field for
// assigning the children to a group.
if ($this->moduleHandler->moduleExists('farm_group')) {
$form['group'] = [
'#type' => 'details',
'#title' => $this->t('Group'),
'#group' => 'tabs',
];
$form['group']['group'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Assign to group'),
'#description' => $this->t('This will make each child a member of the selected group.'),
'#target_type' => 'asset',
'#selection_settings' => [
'target_bundles' => ['group'],
'sort' => [
'field' => 'status',
'direction' => 'ASC',
],
],
];
}
// Birth notes.
$form['notes'] = [
'#type' => 'details',
'#title' => $this->t('Notes'),
'#group' => 'tabs',
];
$form['notes']['notes'] = [
'#type' => 'text_format',
'#title' => $this->t('Notes about the overall birth process'),
'#format' => 'default',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Iterate over the children.
foreach ($form_state->getValue('children') as $delta => $child) {
// Each child must have a name or tag ID.
if (empty($child['name']) && empty($child['tag']['id'])) {
$form_state->setError($form['children'][$delta]['name'], $this->t('The child must have a name or tag ID.'));
}
}
// A mother (either birth or genetic) must be selected.
if (empty($form_state->getValue('birth_mother')) && empty($form_state->getValue('genetic_mother'))) {
$form_state->setError($form['lineage']['birth_mother'], $this->t('A mother animal must be selected.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Get the birthdate.
/** @var \Drupal\Core\Datetime\DrupalDateTime $birthdate */
$birthdate = $form_state->getValue('date');
// Load the mother and father asset(s).
/** @var \Drupal\asset\Entity\AssetInterface|null $birth_mother */
$birth_mother = NULL;
if ($form_state->getValue('birth_mother')) {
$birth_mother = $this->entityTypeManager->getStorage('asset')->load($form_state->getValue('birth_mother'));
}
/** @var \Drupal\asset\Entity\AssetInterface|null $genetic_mother */
$genetic_mother = NULL;
if ($form_state->getValue('genetic_mother')) {
$genetic_mother = $this->entityTypeManager->getStorage('asset')->load($form_state->getValue('genetic_mother'));
}
/** @var \Drupal\asset\Entity\AssetInterface|null $genetic_father */
$genetic_father = NULL;
if ($form_state->getValue('genetic_father')) {
$genetic_father = $this->entityTypeManager->getStorage('asset')->load($form_state->getValue('genetic_father'));
}
// If there is no birth mother, assume that the genetic mother is the birth
// mother. Likewise, if there is no genetic mother, assume that the birth
// mother is the genetic mother. We validate that one of them must exist
// above.
if (empty($birth_mother)) {
$birth_mother = $genetic_mother;
}
if (empty($genetic_mother)) {
$genetic_mother = $birth_mother;
}
// Assemble the list of genetic parents.
$parents = [$genetic_mother];
if (!empty($genetic_father)) {
$parents[] = $genetic_father;
}
// Iterate over the children and create an asset for each.
$children = [];
foreach ($form_state->getValue('children') as $child) {
// Draft a new animal asset for the child.
$asset_values = [
'type' => 'animal',
'name' => !empty($child['name']) ? $child['name'] : $child['tag']['id'],
'animal_type' => $genetic_mother->get('animal_type')->referencedEntities(),
'parent' => $parents,
'birthdate' => $birthdate->getTimestamp(),
'status' => !empty($child['survived']) ? 'active' : 'archived',
];
// Set the sex, if available.
if (!empty($child['sex'])) {
$asset_values['sex'] = $child['sex'];
}
// Set the ID tag, if available.
if (!empty($child['tag']['type']) || !empty($child['tag']['id']) || !empty($child['tag']['location'])) {
$asset_values['id_tag'] = [
[
'type' => $child['tag']['type'],
'id' => $child['tag']['id'],
'location' => $child['tag']['location'],
],
];
}
// Set the child notes, if available.
if (!empty($child['notes']['value'])) {
$asset_values['notes'] = $child['notes'];
}
// Create the child animal asset and add it to the list.
$asset = $this->createAsset($asset_values);
$children[] = $asset;
// If a birth weight was specified, create a weight observation log.
if (!empty($child['weight'])) {
$this->createLog([
'type' => 'observation',
'timestamp' => $birthdate->getTimestamp(),
'name' => $this->t('Weight of @asset is @weight @units', ['@asset' => $asset->label(), '@weight' => $child['weight'], '@units' => $this->birthWeightUnits()]),
'asset' => [$asset],
'quantity' => [
[
'type' => 'standard',
'measure' => 'weight',
'value' => $child['weight'],
'units' => $this->birthWeightUnits(),
],
],
'status' => 'done',
]);
}
}
// Draft birth log values.
$birth_log_values = [
'type' => 'birth',
'timestamp' => $birthdate->getTimestamp(),
'asset' => $children,
'mother' => [$birth_mother],
'notes' => $form_state->getValue('notes'),
'status' => 'done',
];
// Generate the birth log name.
$child_names = [];
foreach ($children as $child) {
$child_names[] = $child->label();
}
$birth_log_values['name'] = $this->t('Birth: @children', ['@children' => $this->trimString(implode(', ', $child_names), 180)]);
// If the birth mother has a location (at the time of birth), use the birth
// log to set the location of the children.
$location = $this->assetLocation->getLocation($birth_mother, $birthdate->getTimestamp());
if ($location) {
$birth_log_values['location'] = $location;
$birth_log_values['is_movement'] = TRUE;
}
// If the group module is enabled, check to see if a group was selected, or
// if the birth mother is in a group (at the time of the birth), make the
// log into a group assignment log that references the group.
if ($this->moduleHandler->moduleExists('farm_group')) {
$group = $form_state->getValue('group');
if (!empty($group)) {
$group = [$this->entityTypeManager->getStorage('asset')->load($group)];
}
if (empty($group)) {
$group = $this->groupMembership->getGroup($birth_mother, $birthdate->getTimestamp());
}
if (!empty($group)) {
$birth_log_values['group'] = $group;
$birth_log_values['is_group_assignment'] = TRUE;
}
}
// Save the birth log.
$this->createLog($birth_log_values);
}
/**
* Ajax callback for children fields.
*/
public function childrenCallback(array $form, FormStateInterface $form_state) {
return $form['children'];
}
/**
* Helper function for getting the birth weight units.
*
* @return string
* The units name, depending on the system of measurement.
*/
protected function birthWeightUnits() {
$quantity_settings = $this->configFactory->get('quantity.settings');
if ($quantity_settings->get('system_of_measurement') == 'us') {
return 'lbs';
}
return 'kg';
}
}

View File

@ -0,0 +1,265 @@
<?php
namespace Drupal\Tests\farm_quick_birth\Kernel;
use Drupal\asset\Entity\Asset;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\log\Entity\Log;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\farm_quick\Kernel\QuickFormTestBase;
/**
* Tests for farmOS birth quick form.
*
* @group farm
*/
class QuickBirthTest extends QuickFormTestBase {
/**
* Quick form ID.
*
* @var string
*/
protected $quickFormId = 'birth';
/**
* Asset location service.
*
* @var \Drupal\farm_location\AssetLocationInterface
*/
protected $assetLocation;
/**
* Group membership service.
*
* @var \Drupal\farm_group\GroupMembershipInterface
*/
protected $groupMembership;
/**
* {@inheritdoc}
*/
protected static $modules = [
'farm_animal',
'farm_animal_type',
'farm_birth',
'farm_group',
'farm_id_tag',
'farm_land',
'farm_observation',
'farm_parent',
'farm_quantity_standard',
'farm_quick_birth',
'farm_unit',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->assetLocation = \Drupal::service('asset.location');
$this->groupMembership = \Drupal::service('group.membership');
$this->installConfig([
'farm_animal',
'farm_animal_type',
'farm_birth',
'farm_group',
'farm_id_tag',
'farm_land',
'farm_observation',
'farm_quantity_standard',
]);
}
/**
* Test birth quick form submission.
*/
public function testQuickBirth() {
// Get today's date.
$today = new DrupalDateTime('midnight');
// Create two animal breeds.
$breed1 = Term::create([
'name' => 'Breed 1',
'vid' => 'animal_type',
]);
$breed1->save();
$breed2 = Term::create([
'name' => 'Breed 2',
'vid' => 'animal_type',
]);
$breed2->save();
// Create birth mother, genetic mother, and genetic father animal assets.
$birth_mother = Asset::create([
'name' => 'Birth Mother',
'type' => 'animal',
'animal_type' => $breed1,
'sex' => 'F',
'status' => 'active',
]);
$birth_mother->save();
$genetic_mother = Asset::create([
'name' => 'Genetic Mother',
'type' => 'animal',
'animal_type' => $breed2,
'sex' => 'F',
'status' => 'active',
]);
$genetic_mother->save();
$genetic_father = Asset::create([
'name' => 'Genetic Father',
'type' => 'animal',
'animal_type' => $breed1,
'sex' => 'M',
'status' => 'active',
]);
$genetic_father->save();
// Create a location asset and move the birth mother there via a log with
// a timestamp of yesterday.
$location = Asset::create([
'name' => 'Field A',
'type' => 'land',
'land_type' => 'field',
'is_fixed' => TRUE,
'is_location' => TRUE,
'status' => 'active',
]);
$location->save();
$movement = Log::create([
'type' => 'observation',
'timestamp' => $today->getTimestamp() - 86400,
'asset' => [$birth_mother],
'location' => [$location],
'is_movement' => TRUE,
'status' => 'done',
]);
$movement->save();
// Create a group asset.
$group = Asset::create([
'name' => 'Herd 1',
'type' => 'group',
'status' => 'active',
]);
$group->save();
// Submit the birth quick form.
$this->submitQuickForm([
'date' => [
'date' => $today->format('Y-m-d'),
'time' => $today->format('H:i:s'),
],
'child_count' => 2,
'children' => [
[
'name' => 'Child 1',
'tag' => [
'id' => '123',
'type' => 'ear_tag',
'location' => 'Left ear',
],
'sex' => 'F',
'weight' => '10',
'notes' => [
'value' => 'Child 1 notes',
'format' => 'default',
],
'survived' => TRUE,
],
[
'name' => 'Child 2',
// A checkbox with a #default_value of TRUE must pass NULL in order
// to be treated as FALSE due to the core checkbox element value
// callback logic. Setting this to FALSE or 0 does not work.
// @see \Drupal\Core\Render\Element\Checkbox::valueCallback()
'survived' => NULL,
],
],
'birth_mother' => $birth_mother->label(),
'genetic_mother' => $genetic_mother->label(),
'genetic_father' => $genetic_father->label(),
'group' => $group->label(),
'notes' => [
'value' => 'Birth notes',
'format' => 'default',
],
]);
// Load assets and logs.
$assets = $this->assetStorage->loadMultiple();
$logs = $this->logStorage->loadMultiple();
// Confirm that seven assets (5 animals + 1 land + 1 group) and three logs
// (1 birth + 2 observations) exists.
$this->assertCount(7, $assets);
$this->assertCount(3, $logs);
// Confirm that the first child animal asset contains all the expected data.
$child1 = $assets[6];
$this->assertEquals('Child 1', $child1->label());
$this->assertEquals($breed2->id(), $child1->get('animal_type')->target_id);
$this->assertEquals($today->getTimestamp(), $child1->get('birthdate')->value);
$this->assertEquals('F', $child1->get('sex')->value);
$this->assertEquals('ear_tag', $child1->get('id_tag')[0]->type);
$this->assertEquals('123', $child1->get('id_tag')[0]->id);
$this->assertEquals('Left ear', $child1->get('id_tag')[0]->location);
$parents = $child1->get('parent')->referencedEntities();
$this->assertCount(2, $parents);
$this->assertEquals($genetic_mother->id(), $parents[0]->id());
$this->assertEquals($genetic_father->id(), $parents[1]->id());
$this->assertEquals('Child 1 notes', $child1->get('notes')->value);
$this->assertEquals('active', $child1->get('status')->value);
$child_location = $this->assetLocation->getLocation($child1);
$this->assertEquals($location->id(), reset($child_location)->id());
$child_group = $this->groupMembership->getGroup($child1);
$this->assertEquals($group->id(), reset($child_group)->id());
// Confirm that the second child animal asset contains all the expected
// data.
$child2 = $assets[7];
$this->assertEquals('Child 2', $child2->label());
$this->assertEquals($breed2->id(), $child2->get('animal_type')->target_id);
$this->assertEquals($today->getTimestamp(), $child2->get('birthdate')->value);
$this->assertEquals('', $child2->get('sex')->value);
$parents = $child2->get('parent')->referencedEntities();
$this->assertCount(2, $parents);
$this->assertEquals($genetic_mother->id(), $parents[0]->id());
$this->assertEquals($genetic_father->id(), $parents[1]->id());
$this->assertEquals('archived', $child2->get('status')->value);
$child_location = $this->assetLocation->getLocation($child2);
$this->assertEquals($location->id(), reset($child_location)->id());
$child_group = $this->groupMembership->getGroup($child2);
$this->assertEquals($group->id(), reset($child_group)->id());
// Confirm that the weight observation log contains all the expected data.
$weight_log = $logs[2];
$this->assertEquals('observation', $weight_log->bundle());
$this->assertEquals($today->getTimestamp(), $weight_log->get('timestamp')->value);
$this->assertEquals('Weight of Child 1 is 10 kg', $weight_log->label());
$this->assertEquals($child1->id(), $weight_log->get('asset')->referencedEntities()[0]->id());
$this->assertEquals('weight', $weight_log->get('quantity')->referencedEntities()[0]->get('measure')->value);
$this->assertEquals('10', $weight_log->get('quantity')->referencedEntities()[0]->get('value')[0]->get('decimal')->getValue());
$this->assertEquals('kg', $weight_log->get('quantity')->referencedEntities()[0]->get('units')->referencedEntities()[0]->get('name')->value);
$this->assertEquals('done', $weight_log->get('status')->value);
// Confirm that the birth log contains all the expected data.
$birth_log = $logs[3];
$this->assertEquals('birth', $birth_log->bundle());
$this->assertEquals($today->getTimestamp(), $birth_log->get('timestamp')->value);
$this->assertEquals('Birth: Child 1, Child 2', $birth_log->label());
$this->assertEquals($child1->id(), $birth_log->get('asset')->referencedEntities()[0]->id());
$this->assertEquals($child2->id(), $birth_log->get('asset')->referencedEntities()[1]->id());
$this->assertEquals($birth_mother->id(), $birth_log->get('mother')->referencedEntities()[0]->id());
$this->assertEquals($location->id(), $birth_log->get('location')[0]->target_id);
$this->assertEquals(TRUE, $birth_log->get('is_movement')->value);
$this->assertEquals($group->id(), $birth_log->get('group')[0]->target_id);
$this->assertEquals(TRUE, $birth_log->get('is_group_assignment')->value);
$this->assertEquals('done', $birth_log->get('status')->value);
$this->assertEquals('Birth notes', $birth_log->get('notes')->value);
}
}