sample code
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
name: Dafter Content
|
||||
description: Provides custom forms for adding content (e.g. Accounts, Orders, Dividend and Trasnsfers).
|
||||
core: 8.x
|
||||
type: module
|
||||
package: Daftar
|
||||
core_version_requirement: ^8 || ^9
|
||||
version: 1.0.0
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- daftar_stock_api:daftar_stock_api
|
||||
@@ -0,0 +1,16 @@
|
||||
daftar_content.account_form:
|
||||
path: '/portfolio/add/account/{type}'
|
||||
defaults:
|
||||
_form: '\Drupal\daftar_content\Form\AccountForm'
|
||||
_title: 'Add an Account'
|
||||
requirements:
|
||||
_role: 'authenticated'
|
||||
|
||||
daftar_content.modal_form:
|
||||
path: '/portfolio/modal/add/{type}'
|
||||
defaults:
|
||||
_title: 'Content Form Modal'
|
||||
_controller: '\Drupal\daftar_content\Controller\ContentFormModalController::launchModal'
|
||||
requirements:
|
||||
_role: 'authenticated'
|
||||
type: '[a-z]+'
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\daftar_content\Controller;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\OpenModalDialogCommand;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Form\FormBuilder;
|
||||
use Drupal\daftar_content\Form\ContentBaseForm;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides callback methods used for launching modals with content form.
|
||||
* @package Drupal\daftar_content\Controller
|
||||
*/
|
||||
class ContentFormModalController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var FormBuilder
|
||||
*/
|
||||
protected $formBuilder;
|
||||
|
||||
/**
|
||||
* OptionsController constructor.
|
||||
* @param FormBuilder $formBuilder
|
||||
*/
|
||||
public function __construct(FormBuilder $formBuilder) {
|
||||
$this->formBuilder = $formBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('form_builder')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AJAX response with the requested form.
|
||||
* @param string $type
|
||||
* @param int $width
|
||||
* @return AjaxResponse
|
||||
*/
|
||||
public function launchModal($type, $width = 600) {
|
||||
|
||||
$form = self::getFormInfo($type);
|
||||
if (NULL === $form) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$response = new AjaxResponse();
|
||||
$modalForm = $this->formBuilder->getForm($form['class'], $type, ContentBaseForm::DISPLAY_MODE_MODAL);
|
||||
$response->addCommand(new OpenModalDialogCommand($form['title'], $modalForm, ['width' => $width]));
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map id to name of a class that implements the form.
|
||||
* @param string $type
|
||||
* @return array|null
|
||||
*/
|
||||
private static function getFormInfo($type) {
|
||||
switch($type) {
|
||||
case 'brokerage':
|
||||
return [
|
||||
'class' => 'Drupal\daftar_content\Form\AccountForm',
|
||||
'title' => 'Add a Brokerage Account',
|
||||
];
|
||||
case 'bank':
|
||||
return [
|
||||
'class' => 'Drupal\daftar_content\Form\AccountForm',
|
||||
'title' => 'Add a Bank Account',
|
||||
];
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
|
||||
/**
|
||||
* Defines the Dividend Transaction entity.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "transaction_dividend",
|
||||
* label = @Translation("Dividend"),
|
||||
* base_table = "dc_transaction_dividend",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "account" = "aid",
|
||||
* "changed" = "last_updated"
|
||||
* },
|
||||
* )
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
class Dividend extends TransactionEntityBase {
|
||||
|
||||
const TAX_TYPE_QUALIFIED = 10;
|
||||
const TAX_TYPE_ORDINARY = 20;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Define the field properties here.
|
||||
*
|
||||
* Field name, type and size determine the table structure.
|
||||
*
|
||||
* In addition, we can define how the field and its content can be manipulated
|
||||
* in the GUI. The behaviour of the widgets used can be determined here.
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
// Override parent label and description.
|
||||
$fields['transaction_date']->setLabel(t('Date Paid'));
|
||||
$fields['transaction_date']->setDescription(t('Date when this dividend was paid.'));
|
||||
|
||||
$fields['execution_date'] = BaseFieldDefinition::create('datetime')
|
||||
->setLabel(t('Execution Date'))
|
||||
->setDescription(t('Date when this dividend was executed.'))
|
||||
->setSetting('datetime_type', 'date');
|
||||
|
||||
$fields['qualified'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Qualified dividend'))
|
||||
->setDescription(t('A boolean flag indicating qualified dividend classification.'))
|
||||
->setDefaultValue(TRUE);
|
||||
|
||||
$fields['auto_imported'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Auto Imported'))
|
||||
->setDescription(t('A boolean flag indicating whether the dividend was automatically imported.'))
|
||||
->setDefaultValue(FALSE);
|
||||
|
||||
// Entity reference field, holds the reference to a taxonomy term object.
|
||||
$fields['symbol'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Symbol'))
|
||||
->setDescription(t('Stock ticker.'))
|
||||
->setSetting('target_type', 'taxonomy_term')
|
||||
->setSetting('handler', 'default')
|
||||
->setSetting('handler_settings', [
|
||||
'target_bundles' => [
|
||||
'symbols' => 'symbols',
|
||||
],
|
||||
])
|
||||
->setReadOnly(TRUE)
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['quantity'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Number of shares'))
|
||||
->setDescription(t('Number of shares on which dividend was paid.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity\Interfaces;
|
||||
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Defines a common interface for entities that belong to an account.
|
||||
*
|
||||
* An account is a node entity to which all transaction entities are
|
||||
* associated with.
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
interface TransactionEntityAccountInterface {
|
||||
|
||||
/**
|
||||
* Returns the entity's account node entity.
|
||||
*
|
||||
* @return NodeInterface
|
||||
* The account node entity.
|
||||
*/
|
||||
public function getAccount();
|
||||
|
||||
/**
|
||||
* Sets the entity's account node entity.
|
||||
*
|
||||
* @param NodeInterface $account
|
||||
* The account node entity.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccount(NodeInterface $account);
|
||||
|
||||
/**
|
||||
* Returns the entity's account node ID.
|
||||
*
|
||||
* @return int|null
|
||||
* The account node ID, or NULL in case the account ID field has not been set on
|
||||
* the entity.
|
||||
*/
|
||||
public function getAccountId();
|
||||
|
||||
/**
|
||||
* Sets the entity's account node ID.
|
||||
*
|
||||
* @param int $aid
|
||||
* The account node id.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccountId($aid);
|
||||
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity\Interfaces;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a Transaction entity.
|
||||
*
|
||||
* We have this interface so we can join the other interfaces it extends.
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
interface TransactionEntityInterface extends ContentEntityInterface, TransactionEntityAccountInterface, EntityChangedInterface {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
|
||||
/**
|
||||
* Defines the Order Transaction entity.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "transaction_order",
|
||||
* label = @Translation("Order"),
|
||||
* base_table = "dc_transaction_order",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "account" = "aid",
|
||||
* "changed" = "last_updated",
|
||||
* "notes" = "notes"
|
||||
* },
|
||||
* )
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
class Order extends TransactionEntityBase {
|
||||
|
||||
const ACTION_BUY = 10;
|
||||
const ACTION_SELL = 20;
|
||||
const ACTION_SELL_SHORT = 30;
|
||||
const ACTION_BUY_TO_COVER = 40;
|
||||
|
||||
const TYPE_MARKET = 10;
|
||||
const TYPE_LIMIT = 20;
|
||||
const TYPE_STOP_MARKET = 30;
|
||||
const TYPE_STOP_LIMIT = 40;
|
||||
const TYPE_MARKET_ON_CLOSE = 50;
|
||||
const TYPE_TRAILING_STOP_D = 60;
|
||||
const TYPE_TRAILING_STOP_P = 70;
|
||||
|
||||
/**
|
||||
* Maps order action to a constant.
|
||||
*
|
||||
* @param string $action Value to map.
|
||||
*
|
||||
* @return int Returns an integer value greater than zero if successfully mapped.
|
||||
*/
|
||||
public static function mapAction($action) {
|
||||
switch(strtolower($action)) {
|
||||
case 'buy':
|
||||
return static::ACTION_BUY;
|
||||
case 'sell':
|
||||
return static::ACTION_SELL;
|
||||
case 'sell short':
|
||||
return static::ACTION_SELL_SHORT;
|
||||
case 'buy to cover':
|
||||
return static::ACTION_BUY_TO_COVER;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps order type to a constant.
|
||||
*
|
||||
* @param string $type Value to map
|
||||
*
|
||||
* @return int Returns an integer value greater than zero if successfully mapped.
|
||||
*/
|
||||
public static function mapType($type) {
|
||||
switch(strtolower($type)) {
|
||||
case 'market':
|
||||
return static::TYPE_MARKET;
|
||||
case 'limit':
|
||||
return static::TYPE_LIMIT;
|
||||
case 'stop market':
|
||||
return static::TYPE_STOP_MARKET;
|
||||
case 'stop limit':
|
||||
return static::TYPE_STOP_LIMIT;
|
||||
case 'market on close':
|
||||
return static::TYPE_MARKET_ON_CLOSE;
|
||||
case 'trailing stop $':
|
||||
return static::TYPE_TRAILING_STOP_D;
|
||||
case 'trailing stop %':
|
||||
return static::TYPE_TRAILING_STOP_P;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Define the field properties here.
|
||||
*
|
||||
* Field name, type and size determine the table structure.
|
||||
*
|
||||
* In addition, we can define how the field and its content can be manipulated
|
||||
* in the GUI. The behaviour of the widgets used can be determined here.
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['action'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Order Action'))
|
||||
->setDescription(t('The action being performed by this order.'))
|
||||
->setSetting('size', 'tiny')
|
||||
->setSetting('unsigned', TRUE)
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['type'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Order Type'))
|
||||
->setDescription(t('The type of order being executed.'))
|
||||
->setSetting('size', 'tiny')
|
||||
->setSetting('unsigned', TRUE);
|
||||
|
||||
// Entity reference field, holds the reference to a taxonomy term object.
|
||||
$fields['symbol'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Symbol'))
|
||||
->setDescription(t('Stock ticker.'))
|
||||
->setSetting('target_type', 'taxonomy_term')
|
||||
->setSetting('handler', 'default')
|
||||
->setSetting('handler_settings', [
|
||||
'target_bundles' => [
|
||||
'symbols' => 'symbols',
|
||||
],
|
||||
])
|
||||
->setReadOnly(TRUE)
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['quantity'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Quantity'))
|
||||
->setDescription(t('Number of shares exchanged.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['on_hand'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Quantity On Hand'))
|
||||
->setDescription(t('Number of shares currently on hand.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2);
|
||||
|
||||
$fields['pps'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Price per Share'))
|
||||
->setDescription(t('Price paid per share (computed).'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setComputed(TRUE);
|
||||
|
||||
$fields['commission'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Commission'))
|
||||
->setDescription(t('Commission charged by the brokerage.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setDefaultValue(0);
|
||||
|
||||
$fields['fees'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Fees'))
|
||||
->setDescription(t('Fees charged for the transaction.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setDefaultValue(0);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity\Traits;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\daftar_content\Entity\Interfaces\TransactionEntityAccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
|
||||
trait TransactionAccountTrait {
|
||||
|
||||
/**
|
||||
* Returns an array of base field definitions for entity account.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type to add the account field to.
|
||||
*
|
||||
* @return \Drupal\Core\Field\BaseFieldDefinition[]
|
||||
* An array of base field definitions.
|
||||
*
|
||||
* @throws \Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException
|
||||
* Thrown when the entity type does not implement TransactionEntityAccountInterface or
|
||||
* if it does not have an "account" entity key.
|
||||
*/
|
||||
public static function accountBaseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
if (!is_subclass_of($entity_type->getClass(), TransactionEntityAccountInterface::class)) {
|
||||
throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not implement \Drupal\daftar_content\Entity\Interfaces\TransactionEntityAccountInterface.');
|
||||
}
|
||||
if (!$entity_type->hasKey('account')) {
|
||||
throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not have an "account" entity key.');
|
||||
}
|
||||
|
||||
return [
|
||||
// Account field of the transaction.
|
||||
// Entity reference field, holds the reference to a account node object.
|
||||
$entity_type->getKey('account') => BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(new TranslatableMarkup('Account ID'))
|
||||
->setDescription(new TranslatableMarkup('The id of the account node this transaction is associated with.'))
|
||||
->setSetting('target_type', 'node')
|
||||
->setSetting('handler', 'default')
|
||||
->setSetting('handler_settings', [
|
||||
'target_bundles' => [
|
||||
'account' => 'account',
|
||||
],
|
||||
])
|
||||
->setReadOnly(TRUE),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's account node entity.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The account node entity.
|
||||
*/
|
||||
public function getAccount() {
|
||||
$key = $this->getEntityType()->getKey('account');
|
||||
return $this->get($key)->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity's account node entity.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $account
|
||||
* The account node entity.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccount(NodeInterface $account) {
|
||||
$key = $this->getEntityType()->getKey('account');
|
||||
$this->set($key, $account);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity's account node ID.
|
||||
*
|
||||
* @return int|null
|
||||
* The account node ID, or NULL in case the account ID field has not been set on
|
||||
* the entity.
|
||||
*/
|
||||
public function getAccountId() {
|
||||
return $this->getEntityKey('account');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity's account node ID.
|
||||
*
|
||||
* @param int $aid
|
||||
* The account node id.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccountId($aid) {
|
||||
$key = $this->getEntityType()->getKey('account');
|
||||
$this->set($key, $aid);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityChangedTrait;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\daftar_content\Entity\Interfaces\TransactionEntityInterface;
|
||||
use Drupal\daftar_content\Entity\Traits\TransactionAccountTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Implements Transaction Entity specific enhancements to the Content Entity Base class.
|
||||
*
|
||||
* @TODO Add transaction_id (for mapping to brokerage) and notes fields in
|
||||
* a separate tables.
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
class TransactionEntityBase extends ContentEntityBase implements TransactionEntityInterface {
|
||||
|
||||
use EntityChangedTrait;
|
||||
use TransactionAccountTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Define the field properties here.
|
||||
*
|
||||
* Field name, type and size determine the table structure.
|
||||
*
|
||||
* In addition, we can define how the field and its content can be manipulated
|
||||
* in the GUI. The behaviour of the widgets used can be determined here.
|
||||
* @throws \Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
$fields += static::accountBaseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['transaction_date'] = BaseFieldDefinition::create('datetime')
|
||||
->setLabel(t('Transaction Date'))
|
||||
->setDescription(t('Date when this transaction took place.'))
|
||||
->setSetting('datetime_type', 'date')
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['amount'] = BaseFieldDefinition::create('decimal')
|
||||
->setLabel(t('Amount'))
|
||||
->setDescription(t('Total transaction amount.'))
|
||||
->setSetting('precision', 10)
|
||||
->setSetting('scale', 2)
|
||||
->setRequired(TRUE);
|
||||
|
||||
if ($entity_type->hasKey('notes')) {
|
||||
$notesKey = $entity_type->getKey('notes');
|
||||
$fields[$notesKey] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Notes'))
|
||||
->setDescription(t('Short descriptive text for future references.'))
|
||||
->setSetting('max_length', 255);
|
||||
}
|
||||
|
||||
if ($entity_type->hasKey('changed')) {
|
||||
$changedKey = $entity_type->getKey('changed');
|
||||
$fields[$changedKey] = BaseFieldDefinition::create('changed')
|
||||
->setLabel(t('Last Updated'))
|
||||
->setDescription(t('The time that the transaction was last updated.'))
|
||||
->setReadOnly(TRUE);
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Drupal\daftar_content\Entity;
|
||||
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Defines the Transfer Transaction entity.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "transaction_transfer",
|
||||
* label = @Translation("Transfer"),
|
||||
* base_table = "dc_transaction_transfer",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "uuid" = "uuid",
|
||||
* "account" = "aid",
|
||||
* "changed" = "last_updated",
|
||||
* "notes" = "notes"
|
||||
* },
|
||||
* )
|
||||
*
|
||||
* @ingroup transaction_entity
|
||||
*/
|
||||
class Transfer extends TransactionEntityBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Define the field properties here.
|
||||
*
|
||||
* Field name, type and size determine the table structure.
|
||||
*
|
||||
* In addition, we can define how the field and its content can be manipulated
|
||||
* in the GUI. The behaviour of the widgets used can be determined here.
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
// Override parent label and description.
|
||||
if ($entity_type->hasKey('account')) {
|
||||
$key = $entity_type->getKey('account');
|
||||
$fields[$key]->setLabel(t('To'));
|
||||
$fields[$key]->setDescription(t('The Account to which the amount was deposited.'));
|
||||
}
|
||||
|
||||
// Account field of the transaction.
|
||||
// Entity reference field, holds the reference to a account node object.
|
||||
$fields['account_withdrawn'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('From'))
|
||||
->setDescription(t('The Account from which the amount was withdrawn.'))
|
||||
->setSetting('target_type', 'node')
|
||||
->setSetting('handler', 'default')
|
||||
->setSetting('handler_settings', [
|
||||
'target_bundles' => [
|
||||
'account' => 'account',
|
||||
],
|
||||
])
|
||||
->setRequired(TRUE)
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\daftar_content\Form;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\OpenModalDialogCommand;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class AccountForm extends ContentBaseForm {
|
||||
|
||||
const MIN_LENGTH_ALIAS = 5;
|
||||
const MAX_LENGTH_ALIAS = 30;
|
||||
const MAX_LENGTH_NOTE = 150;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'daftar_content_brokerage_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $type = 'brokerage', $display_mode = self::DISPLAY_MODE_DEFAULT) {
|
||||
$type = strtolower($type);
|
||||
if($type !== 'brokerage' && $type != 'bank') {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$form_state->set('type', $type);
|
||||
$fields = $this->getFieldDefinitions('account');
|
||||
|
||||
// Get anything we need from the base class.
|
||||
$form = parent::buildForm($form, $form_state, $display_mode);
|
||||
|
||||
$form['title'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $fields['title']->getLabel(),
|
||||
'#description' => $this->t('%title for the account. Do NOT enter your account number.', ['%title' => $fields['title']->getLabel()]),
|
||||
'#maxlength' => self::MAX_LENGTH_ALIAS,
|
||||
'#pattern' => '^[a-zA-Z0-9 ]{5,}$',
|
||||
'#attributes' => ['title' => $this->t('Must be at least five characters long. Only alphanumeric and space characters are supported.')],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
switch($display_mode) {
|
||||
case self::DISPLAY_MODE_EMBEDDED:
|
||||
break;
|
||||
|
||||
case self::DISPLAY_MODE_DEFAULT:
|
||||
default:
|
||||
// set page title
|
||||
$form['#title'] = ($type === 'brokerage')
|
||||
? $this->t('Add a Brokerage Account')
|
||||
: $this->t('Add a Bank Account');
|
||||
|
||||
// no break - intentionally left off
|
||||
|
||||
case self::DISPLAY_MODE_MODAL:
|
||||
if (isset($fields['field_notes'])) {
|
||||
$form['field_notes'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $fields['field_notes']->getLabel(),
|
||||
'#description' => $this->t('Max characters allowed: %max', ['%max' => self::MAX_LENGTH_NOTE]),
|
||||
'#maxlength' => self::MAX_LENGTH_NOTE,
|
||||
'#rows' => 2,
|
||||
'#resizable' => 'none',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
|
||||
$type = $form_state->get('type');
|
||||
$title = trim($form_state->getValue('title'));
|
||||
|
||||
// @todo change to custom exception
|
||||
if($type !== 'brokerage' && $type !== 'bank') {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$titleLabel = $form['title']['#title'];
|
||||
if (strlen($title) < self::MIN_LENGTH_ALIAS) {
|
||||
$form_state->setErrorByName('title', $this->t(
|
||||
'%label must be at least %min characters long.',
|
||||
['%label' => $titleLabel, '%min' => self::MIN_LENGTH_ALIAS]
|
||||
));
|
||||
}
|
||||
elseif (preg_match('/[^a-zA-Z0-9 $]/', $title)) {
|
||||
$form_state->setErrorByName('title', $this->t(
|
||||
'%label contains invalid characters.',
|
||||
['%label' => $titleLabel]
|
||||
));
|
||||
}
|
||||
elseif ($this->isDuplicate($title)) {
|
||||
$form_state->setErrorByName('title', $this->t(
|
||||
'An account already exists under your portfolio with given alias.',
|
||||
['%label' => $titleLabel]
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
|
||||
$type = $form_state->get('type');
|
||||
$title = trim($form_state->getValue('title'));
|
||||
$notes = substr(trim($form_state->getValue('field_notes')), 0, self::MAX_LENGTH_NOTE);
|
||||
|
||||
$node = $this->nodeStorage->create([
|
||||
'type' => 'account',
|
||||
'status' => TRUE,
|
||||
'uid' => $this->currentUser()->id(),
|
||||
'title' => $title,
|
||||
'field_account_status' => TRUE,
|
||||
'field_account_type' => ($type == 'brokerage') ? 'br' : 'ba',
|
||||
'field_notes' => $notes
|
||||
]);
|
||||
|
||||
//@TODO improve for AJAX and embedded form.
|
||||
// Remove message.
|
||||
try {
|
||||
$saveOperation = $node->save();
|
||||
if ($saveOperation == SAVED_NEW) {
|
||||
$this->messenger()->addMessage(
|
||||
$this->t('Created %type account: %title', [
|
||||
'%type' => $form_state->get('type'),
|
||||
'%title' => $title,
|
||||
]),
|
||||
MessengerInterface::TYPE_STATUS
|
||||
);
|
||||
$form_state->set('account_node', $node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->messenger()->addMessage(
|
||||
$this->t('Unable to create account. Please try again.'),
|
||||
MessengerInterface::TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
$form_state->set('account_node', NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function submitFormAjaxAdd(array &$form, FormStateInterface $form_state) {
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new OpenModalDialogCommand("Success!", 'The modal form has been submitted.'));
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function isDuplicate($title) {
|
||||
$title = strtolower($title);
|
||||
$isDuplicate = FALSE;
|
||||
$results = $this->getAccountNids();
|
||||
|
||||
if (!empty($results)) {
|
||||
$nodes = $this->nodeStorage->loadMultiple($results);
|
||||
foreach ($nodes as $nid => $node) {
|
||||
if ( $title === strtolower(trim($node->label())) ) {
|
||||
$isDuplicate = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $isDuplicate;
|
||||
}
|
||||
|
||||
protected function getAccountNids() {
|
||||
return $this->nodeStorage->getQuery()
|
||||
->condition('status', 1)
|
||||
->condition('type', 'account')
|
||||
->condition('uid', $this->currentUser()->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\daftar_content\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\node\NodeStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
abstract class ContentBaseForm extends FormBase {
|
||||
|
||||
const DISPLAY_MODE_DEFAULT = 'default';
|
||||
const DISPLAY_MODE_MODAL = 'modal';
|
||||
const DISPLAY_MODE_EMBEDDED = 'embedded';
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
public function __construct(EntityFieldManagerInterface $entity_field_manager, NodeStorageInterface $node_storage) {
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->nodeStorage = $node_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager')->getStorage('node')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for AJAX submit. Deriving class must implement this method.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @return array
|
||||
* Renderable array (the box element)
|
||||
*/
|
||||
public abstract function submitFormAjaxAdd(array &$form, FormStateInterface $form_state);
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $display_mode = self::DISPLAY_MODE_DEFAULT) {
|
||||
|
||||
$form['actions'] = [
|
||||
'#type' => 'actions',
|
||||
'#weight' => 100,
|
||||
];
|
||||
|
||||
switch($display_mode) {
|
||||
case self::DISPLAY_MODE_EMBEDDED:
|
||||
unset($form['actions']);
|
||||
break;
|
||||
|
||||
case self::DISPLAY_MODE_MODAL:
|
||||
$form['status_messages'] = [
|
||||
'#type' => 'status_messages',
|
||||
'#weight' => -100,
|
||||
];
|
||||
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Add'),
|
||||
'#attributes' => [
|
||||
'class' => [
|
||||
'use-ajax',
|
||||
],
|
||||
],
|
||||
'#ajax' => [
|
||||
'callback' => [$this, 'submitFormAjaxAdd'],
|
||||
'event' => 'click',
|
||||
],
|
||||
];
|
||||
|
||||
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
|
||||
|
||||
break;
|
||||
|
||||
case self::DISPLAY_MODE_DEFAULT:
|
||||
default:
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Add'),
|
||||
];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $node_bundle
|
||||
* The bundle (e.g. account, order, dividend & etc).
|
||||
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
|
||||
*/
|
||||
protected function getFieldDefinitions($node_bundle) {
|
||||
return $this->entityFieldManager->getFieldDefinitions('node', $node_bundle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user