Magento 2 How to get shipping method amount from Checkout/cart/index page Sidebar radio button selection - adobe

Query- I need to develop a functionality in which custom fee charge on order based upon shipping amount.
I got correct amount in fetch function when select different shipping method. But incorrect amount get in collect function when switch shipping method.
So how can we get correct shipping method amount based upon shipping method selected.
<?php
namespace Nikunj\CustomFee\Model\Quote;
use Magento\Quote\Api\Data\ShippingAssignmentInterface as ShippingAssignmentInterfaceAlias;
class Customfee extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal
{
/**
* Collect grand total address amount
*
* #param \Magento\Quote\Model\Quote $quote
* #param ShippingAssignmentInterfaceAlias $shippingAssignment
* #param \Magento\Quote\Model\Quote\Address\Total $total
* #return $this
*/
protected $quoteValidator = null;
public function __construct(\Magento\Quote\Model\QuoteValidator $quoteValidator)
{
$this->quoteValidator = $quoteValidator;
}
public function collect(
\Magento\Quote\Model\Quote $quote,
ShippingAssignmentInterfaceAlias $shippingAssignment,
\Magento\Quote\Model\Quote\Address\Total $total
) {
parent::collect($quote, $shippingAssignment, $total);
$exist_amount = 0; //$quote->getCustomfee();
$customfee = ($quote->getShippingAddress()->getShippingAmount()+15);
$balance = $customfee - $exist_amount;//final amount
$total->setTotalAmount('customfee', $balance);
$total->setBaseTotalAmount('customfee', $balance);
$total->setCustomfee($balance);
$total->setBaseCustomfee($balance);
$total->setGrandTotal($total->getGrandTotal() + $balance);
$total->setBaseGrandTotal($total->getBaseGrandTotal() + $balance);
return $this;
}
protected function clearValues(Address\Total $total)
{
$total->setTotalAmount('subtotal', 0);
$total->setBaseTotalAmount('subtotal', 0);
$total->setTotalAmount('tax', 0);
$total->setBaseTotalAmount('tax', 0);
$total->setTotalAmount('discount_tax_compensation', 0);
$total->setBaseTotalAmount('discount_tax_compensation', 0);
$total->setTotalAmount('shipping_discount_tax_compensation', 0);
$total->setBaseTotalAmount('shipping_discount_tax_compensation', 0);
$total->setSubtotalInclTax(0);
$total->setBaseSubtotalInclTax(0);
}
/**
* #param \Magento\Quote\Model\Quote $quote
* #param Address\Total $total
* #return array|null
*/
/**
* Assign subtotal amount and label to address object
*
* #param \Magento\Quote\Model\Quote $quote
* #param Address\Total $total
* #return array
*/
public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total)
{
$fee = ($quote->getShippingAddress()->getShippingAmount()+15);
return [
'code' => 'customfee',
'title' => 'Custom Fee',
'value' => $fee
];
}
/**
* Get Subtotal label
*
* #return \Magento\Framework\Phrase
*/
public function getLabel()
{
return __('Custom Fee');
}
}

To can recalculate the fee value on the front-end side asynchronously you have to implement some changes on checkout as described here: https://magento.stackexchange.com/a/93349
also, you can check this module:
https://github.com/sivajik34/Custom-Fee-Magento2

Related

Magento 2 Create Shipping Method with Delivery Options

I created a new shipping method for home delivery and I want to be able to present options such as a building/unit/room number. My thought was when the user selected this method the options would appear with a text field for the user to enter information before moving to the payment page. Any guidance on the best way to make this happen?
This is in Model/Carrier
/**
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
* #param \Psr\Log\LoggerInterface $logger
* #param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
* #param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
array $data = []
) {
$this->_rateResultFactory = $rateResultFactory;
$this->_rateMethodFactory = $rateMethodFactory;
$this->_logger = $logger;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* #param RateRequest $request
* #return \Magento\Shipping\Model\Rate\Result|bool
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
/** #var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->_rateResultFactory->create();
$shippingPrice = $this->getConfigData('price');
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod($this->_code);
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice($shippingPrice);
$method->setCost($shippingPrice);
$result->append($method);
return $result;
}
/**
* #return array
*/
public function getAllowedMethods()
{
return [$this->_code=> $this->getConfigData('name')];
}
I tried to create checkout_index_index.xml and add a custom phtml page to it with
<script type="text/javascript">
require([
'jquery',
'Magento_Checkout/js/model/quote',
], function (jQuery, quote) {
jQuery(document).ready(function () {
quote.shippingMethod.subscribe(function (value) {
if (quote.shippingMethod() && quote.shippingMethod().carrier_code == 'your_custom_shipping_method_code') {
var customBlock = "<div class ='custom-information'><input type="text" id="your_custom_id"></div>";
if((!$('.custom-information').length > 0)) {
$('#checkout-shipping-method-load').append(customBlock);
}
});
});
});
});
</script>
And then I added in Model
namespace Magento\Checkout\Model;
class GuestShippingInformationManagement implements \Magento\Checkout\Api\GuestShippingInformationManagementInterface
{
/**
* #var \Magento\Quote\Model\QuoteIdMaskFactory
*/
protected $quoteIdMaskFactory;
/**
* #var \Magento\Checkout\Api\ShippingInformationManagementInterface
*/
protected $shippingInformationManagement;
/**
* #param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory
* #param \Magento\Checkout\Api\ShippingInformationManagementInterface $shippingInformationManagement
* #codeCoverageIgnore
*/
public function __construct(
\Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
\Magento\Checkout\Api\ShippingInformationManagementInterface $shippingInformationManagement
) {
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
$this->shippingInformationManagement = $shippingInformationManagement;
}
/**
* {#inheritDoc}
*/
public function saveAddressInformation(
$cartId,
\Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
) {
/** #var $quoteIdMask \Magento\Quote\Model\QuoteIdMask */
$quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id');
return $this->shippingInformationManagement->saveAddressInformation(
$quoteIdMask->getQuoteId(),
$addressInformation
);
}
}
and started to modify it but I think I may be headed in the wrong direction,
Thoughts on the best way to accomplish this?
Thanks!

Override Method or EventListener: stop creation process and show warning just the first time in EasyAdmin?

I am using EasyAdmin in my SF 3.3 project but I need to achieve something different from how EasyAdmin has been built for. Take a look at the following picture:
As you might notice a user can be in more than one GroupingRole. Having that information the challenge is:
Check if the user has been assigned to any other GroupingRole
If the criteria meets the condition then show a warning message saying "The user A is already assigned to GroupingRole A" and prevent the record to be created. (this message could be in a popup, a javascript alert or an alert from Bootstrap - since EA already uses it)
When the admin click once again on "Save changes" the record should be created.
What I want to achieve with this approach is to alert the admin that the user is already to any other group but not stop him for create the record.
I have achieve some part of it already by override the prePersist method for just that entity (see below):
class AdminController extends BaseAdminController
{
/**
* Check if the users has been assigned to any group
*/
protected function prePersistGroupingRoleEntity($entity)
{
$usersToGroupRoleEntities = $this->em->getRepository('CommonBundle:UsersToGroupRole')->findAll();
$usersToGroupRole = [];
/** #var UsersToGroupRole $groupRole */
foreach ($usersToGroupRoleEntities as $groupRole) {
$usersToGroupRole[$groupRole->getGroupingRoleId()][] = $groupRole->getUsersId();
}
$usersInGroup = [];
/** #var Users $userEntity */
foreach ($entity->getUsersInGroup() as $userEntity) {
foreach ($usersToGroupRole as $group => $users) {
if (\in_array($userEntity->getId(), $users, true)) {
$usersInGroup[$group][] = $userEntity->getId();
}
}
}
$groupingRoleEnt = $this->em->getRepository('CommonBundle:GroupingRole');
$usersEnt = $this->em->getRepository('CommonBundle:Users');
$message = [];
foreach ($usersInGroup as $group => $user) {
foreach($user as $usr) {
$message[] = sprintf(
'The user %s already exists in %s group!',
$usersEnt->find($usr)->getEmail(),
$groupingRoleEnt->find($group)->getName()
);
}
}
}
}
What I don't know is how to stop the record to be created and instead show the warning just the first time the button is clicked because the second time and having the warning in place I should allow to create the record.
Can any give me some ideas and/or suggestions?
UPDATE: adding entities information
In addition to the code displayed above here is the entities involved in such process:
/**
* #ORM\Entity
* #ORM\Table(name="grouping_role")
*/
class GroupingRole
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="role_name", type="string", nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="role_description", type="string", nullable=false)
*/
private $description;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Schneider\QuoteBundle\Entity\Distributor", inversedBy="groupingRole")
* #ORM\JoinTable(name="grouping_to_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="DistributorID", referencedColumnName="DistributorID", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one Distributor"
* )
*/
private $distributorGroup;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="CommonBundle\Entity\Users", inversedBy="usersGroup")
* #ORM\JoinTable(name="users_to_group_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="users_id", referencedColumnName="users_id", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one user"
* )
*/
private $usersInGroup;
/**
* Constructor
*/
public function __construct()
{
$this->distributorGroup = new ArrayCollection();
$this->usersInGroup = new ArrayCollection();
}
}
/**
* #ORM\Entity()
* #ORM\Table(name="users_to_group_role")
*/
class UsersToGroupRole
{
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer",nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $usersId;
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer", nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $groupingRoleId;
}
A little example by using form validation approach in EasyAdminBundle:
class AdminController extends EasyAdminController
{
// ...
protected function create<EntityName>EntityFormBuilder($entity, $view)
{
$builder = parent::createEntityFormBuilder($entity, $view);
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$flag = false;
if (isset($data['flag'])) {
$flag = $data['flag'];
unset($data['flag']);
}
$key = md5(json_encode($data));
if ($flag !== $key) {
$event->getForm()->add('flag', HiddenType::class, ['mapped' => false]);
$data['flag'] = $key;
$event->setData($data);
}
});
return $builder;
}
protected function get<EntityName>EntityFormOptions($entity, $view)
{
$options = parent::getEntityFormOptions($entity, $view);
$options['validation_groups'] = function (FormInterface $form) {
if ($form->has('flag')) {
return ['Default', 'CheckUserGroup'];
}
return ['Default'];
};
$options['constraints'] = new Callback([
'callback' => function($entity, ExecutionContextInterface $context) {
// validate here and adds the violation if applicable.
$context->buildViolation('Warning!')
->atPath('<field>')
->addViolation();
},
'groups' => 'CheckUserGroup',
]);
return $options;
}
}
Note that PRE_SUBMIT event is triggered before the validation process happen.
The flag field is added (dynamically) the first time upon submitted the form, so the validation group CheckUserGroup is added and the callback constraint do its job. Later, the second time the submitted data contains the flag hash (if the data does not changes) the flag field is not added, so the validation group is not added either and the entity is saved (same if the callback constraint does not add the violation the first time).
Also (if you prefer) you can do all this inside a custom form type for the target entity.

Payum Stripe data flow with Symfony

I am trying to create a checkout that allows a user to create an account for a fee (premium accounts, if you will). A user will create an account (marked as unpaid), the user will pay, and then on a successful payment the account is marked as paid. I can create an account, and I can make a charge. My problem is linking the two things together. I'm not sure how to reference the created account from the successful charge. Here is what I have so far.
Payment.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Payum\Core\Model\ArrayObject;
/**
* #ORM\Table
* #ORM\Entity
*/
class Payment extends ArrayObject
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #var integer $id
*/
protected $id;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
CreateProfileController.php
CreateAction
public function createASquareAction(Request $request, $coupon)
{
$newUser = new User();
$registrationForm = $this->getRegistrationForm($newUser);
$registrationForm->handleRequest($request);
if ($registrationForm->isSubmitted() && $registrationForm->isValid()) {
$newSquare = new Square();
$newSquare->setProduct($registrationForm->get('product')->getData());
$newUser->addSquare($newSquare);
$cost = $this->getTotal($newSquare->getProduct(), $registrationForm->get('coupon')->getData());
$noPayment = $this->isAdmin() || $cost == 0;
$em = $this->getDoctrine()->getManager();
$em->persist($newUser);
$em->flush();
if ($noPayment) {
$newSquare->setVerified(true);
$em->persist($newSquare);
$em->flush();
return $this->redirectToRoute('edit', array(
'id' => $newSquare->getMsid()
));
} else {
$gatewayName = 'stripe_checkout_test';
$storage = $this->get('payum')->getStorage('AppBundle\Entity\Payment');
$payment = $storage->create();
$payment["amount"] = $cost;
$payment["currency"] = 'USD';
$payment["description"] = "Memorysquare";
$storage->update($payment);
$captureToken = $this->get('payum')->getTokenFactory()->createCaptureToken(
$gatewayName,
$payment,
'test_payment_done' // the route to redirect after capture;
);
return $this->redirect($captureToken->getTargetUrl());
}
}
return $this->render('create-a-square/create-a-square.html.twig', [
'registrationForm' => $registrationForm->createView(),
'coupon' => $coupon,
]);
}
Payment Complete Action
public function testPaymentDoneAction(Request $request)
{
$token = $this->get('payum')->getHttpRequestVerifier()->verify($request);
$identity = $token->getDetails();
$model = $this->get('payum')->getStorage($identity->getClass())->find($identity);
$gateway = $this->get('payum')->getGateway($token->getGatewayName());
// or Payum can fetch the model for you while executing a request (Preferred).
$gateway->execute($status = new GetHumanStatus($token));
$details = $status->getFirstModel();
return new JsonResponse(array(
'status' => $status->getValue(),
'details' => iterator_to_array($details),
));
}
Any help to get me on track would be greatly appreciated.
The answer to this was adding my order entity (or any entity you'd like) to the Payment (or PaymentDetails) entity like so (NOTE the cascade persist):
Payment.php
// ... all previous code ... //
/**
* #ORM\OneToOne(targetEntity="Order", cascade={"persist"})
* #ORM\JoinColumn(name="orderid", referencedColumnName="orderid")
*/
private $order;
/**
* Set order
*
* #param \AppBundle\Entity\Order $order
*
* #return Payment
*/
public function setOrder(\AppBundle\Entity\Order $order = null)
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* #return \AppBundle\Entity\Order
*/
public function getOrder()
{
return $this->order;
}
Then in the payment preparation, I add the new order to the $payment object
public function createASquareAction(Request $request, $coupon)
{
// ... previous code ... //
$newOrder = new Order();
// do some setting on my order
$payment->setOrder($newOrder);
$storage->update($payment);
// ... rest of code ... //
}
Maybe this will help someone in the future. I also created an event subscriber to check the order onUpdate, and mark as paid if the stripe payment was successful.

symfony 2 get Value from manyToOne confusion

I have an entity which looks like
.
.
.
/**
* #ORM\ManyToMany(targetEntity="PrLeadBundle\Entity\Stock")
* #ORM\JoinColumn(name="stock_id", referencedColumnName="id", nullable=true)
*/
private $stock;
public function __construct()
{
$this->stock = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add stock
*
* #param \PrLeadBundle\Entity\Stock $stock
* #return ValueList
*/
public function addStock(\PrLeadBundle\Entity\Stock $stock)
{
$this->stock[] = $stock;
return $this;
}
/**
* Remove stock
*
* #param \PrLeadBundle\Entity\Stock $stock
*/
public function removeStock(\PrLeadBundle\Entity\Stock $stock)
{
$this->stock->removeElement($stock);
}
/**
* Get stock
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getStock()
{
return $this->stock;
}
Now, If I try to access theese values I get an Join Column error.
I'm using :
$Values = $entityManager->getRepository('PrLeadBundle:ValueList')->findBy(array('disabled' => '0','exportable'=>'1'), array('title' => 'ASC'));
foreach ($Values as $item){
var_dump($item->getStock()->getId());
}
This is a bit confusing for me cause i ran into an :
Attempted to call method "getId" on class
"Doctrine\ORM\PersistentCollection". 500 Internal Server Error -
UndefinedMethodException
I did thsi for certain times in past, and I can also access the stock values in twig by using {{ item.stock.id }} ...
What am I doing wrong??
Because $stock variable is a Doctrine\Common\Collections\ArrayCollection,
so instead of $item->getStock()->getId() you should try something like $item->getStock()->first()->getId()); or var_dump($item->getStock() to see the structure before extracting the element.

Sonata User Admin - Custom field dependency

I have extended the SonataAdmin class for FOSUser and added 2 custom fields (choice type from external data source): Company and Sector
I'd like to make Sector dependent on Company, so if the user selects a Company it filters the available Sectors.
I though about using FormEvents for filtering at page load, but I don't even know how to get the Company value of the current form.
Here is a part of my custom SectorType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA
, function(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
// Need to get the company value here if set
});
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->getSectors(),
));
}
public function getSectors()
{
$sects = array();
// Need to pass the selected company value to my getList
// (which gets the list of sector as you can imagine)
if (($tmp_sects = $this->ssrs->getList('Sector'))) {
foreach ($tmp_sects as $sect) {
$label = $sect['id'] ? $sect['label'] : '';
$sects[$sect['id']] = $label;
}
}
return $sects;
}
So the question is:
How to get the selected Company from my custom SectorType ?
After that I'll need to be able to refresh the Sector with Ajax, but that will be another question
I had a similar problem. I needed to create a sale entity that needed to be associated in a many to one relationship with an enterprise entity and a many to many relationship with services entities. Here is the Sale Entity:
The thing is that services where available depending on the companies chosen. For instance services a and b could only be provided to company x. And services b and c could only be provided to company y. So in my admin, depending on the chosen company I had to display the available services. For these I needed to do 2 things:
First create a dynamic form with my sale admin, so that on the server side I could get the right services available for the company specified in my sale record. And second, I had to create a custom form type for my company form element, so that when it was changed by the user on the client side, It would send an ajax request to get the right services for the company chosen.
For my first problem, I did something similar to what you were trying to achieve, but instead of creating an specific custom type for my services element, I added de event listener directly in the admin.
Here is the Sale entity:
/**
*
* #ORM\Table(name="sales")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Sale
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
public $id;
/**
* #ORM\ManyToOne(targetEntity="Branch")
* #ORM\JoinColumn(name="branch_id", referencedColumnName="id", nullable = false)
* #Assert\NotBlank(message = "Debe especificar una empresa a la cual asignar el precio de este exámen!")
*/
private $branch;
/** Unidirectional many to many
* #ORM\ManyToMany(targetEntity="Service")
* #ORM\JoinTable(name="sales_services",
* joinColumns={#ORM\JoinColumn(name="sale_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="service_id", referencedColumnName="id")}
* )
* #Assert\Count(min = "1", minMessage = "Debe especificar al menos un servicio a realizar!")
*/
private $services;
public function __construct() {
$this->services = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set branch
*
* #param Astricom\NeurocienciasBundle\Entity\Branch $branch
*/
//default value always have to be null, because when validation constraint is set to notblank,
//if default is not null, before calling the validation constraint an error will pop up explaining
//that no instance of Branch was passed to the $branch argument.
public function setBranch(\Astricom\NeurocienciasBundle\Entity\Branch $branch = null)
{
$this->branch = $branch;
}
/**
* Get branch
*
* #return Astricom\NeurocienciasBundle\Entity\Branch
*/
public function getBranch()
{
return $this->branch;
}
/**
* Add service
*
* #param \Astricom\NeurocienciasBundle\Entity\Service|null $service
*/
public function addServices(\Astricom\NeurocienciasBundle\Entity\Service $service = null)
{
$this->services[] = $service;
}
/**
* Get services
*
* #return Doctrine\Common\Collections\Collection
*/
public function getServices()
{
return $this->services;
}
/**
* Sets the creation date
*
* #param \DateTime|null $createdAt
*/
public function setCreatedAt(\DateTime $createdAt = null)
{
$this->createdAt = $createdAt;
}
/**
* Returns the creation date
*
* #return \DateTime|null
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Sets the last update date
*
* #param \DateTime|null $updatedAt
*/
public function setUpdatedAt(\DateTime $updatedAt = null)
{
$this->updatedAt = $updatedAt;
}
So then in the Admin form builder:
protected function configureFormFields(FormMapper $formMapper) {
$em = $this->container->get('doctrine')->getEntityManager();
$branchQuery = $em->createQueryBuilder();
$branchQuery->add('select', 'b')
->add('from', 'Astricom\NeurocienciasBundle\Entity\Branch b')
->add('orderBy', 'b.name ASC');
$formMapper
->with('Empresa/Sucursal')
->add('branch','shtumi_ajax_entity_type',array('required' => true, 'label'=>'Empresa/Sucursal','error_bubbling' => true, 'empty_value' => 'Seleccione una empresa/sucursal', 'empty_data' => null, 'entity_alias'=>'sale_branch', 'attr'=>array('add_new'=>false), 'model_manager' => $this->getModelManager(), 'class'=>'Astricom\NeurocienciasBundle\Entity\Branch', 'query' => $branchQuery))
->end()
;
$builder = $formMapper->getFormBuilder();
$factory = $builder->getFormFactory();
$sale = $this->getSubject();
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function(DataEvent $event) use ($sale,$factory, $em) {
$form = $event->getForm();
$servicesQuery = $em->createQueryBuilder();
$servicesQuery->add('select','s')
->add('from','Astricom\NeurocienciasBundle\Entity\Service s');
if (!$sale || !$sale->getId()) {
$servicesQuery
->where($servicesQuery->expr()->eq('s.id', ':id'))
->setParameter('id', 0);
}
else {
$servicesQuery
->join('s.branch', 'b')
->where($servicesQuery->expr()->eq('b.id', ':id'))
->setParameter('id', $sale->getBranch()->getId());
}
$form->add($factory->createNamed('services','entity',null,array('required' => true, 'label'=>'Servicios','error_bubbling' => true, 'attr'=>array('show_value_label'=>true),'class'=>'Astricom\NeurocienciasBundle\Entity\Service','multiple'=>true,'expanded'=>true,'query_builder'=>$servicesQuery)));
}
);
}
The trick thing was to pass the forms data. It doesn't work to use evet->getData() in the event listener's function. Instead I passed it through the admin->getSubject() method. Then instead of adding a sonataadmin form type, inside the event listener's function, I had to use a plain symfony form type.
The Ajax part as you mentioned is another question. All the weird things on the branch add method in the form builder is related to a customized field type for this matter. Don't worry about it.

Resources