Sylius add to cart via CartItemController gives 0,00 total - php

We are currently developing an ERP-based webshop with Sylius. One of the features is that a customer can select multiple sizes and quantities and add them to their cart in one action.
Normally Sylius would work with a request-based ItemResolver for only one variant. We've tried to override the CartItemController so that we would be able to loop the request variables and add all the items to the cart.
We tried to use this code:
try {
// When we have multiple
$reqSize = $request->request->get('size');
$reqQuantity = $request->request->get('quantity');
$reqProductID = $request->request->get('product_id');
$reqColorID = $request->request->get('variant_color_id');
if (null !== $reqSize && null !== $reqQuantity && null !== $reqProductID && null !== $reqColorID && count($reqSize) === count($reqQuantity)) {
$provider = $this->get('sylius.cart_provider'); // Implements the CartProviderInterface.
$currentCart = $provider->getCart();
$priceCalculator = $this->get('sylius.price_calculator');
$availabilityChecker = $this->get('sylius.availability_checker');
$productRepo = $this->get('sylius.repository.product');
$variantRepo = $this->get('sylius.repository.product_variant');
$sizeRepo = $this->get('jartazi.repository.sizegrid_size');
$colorRepo = $this->get('jartazi.repository.color');
$product = $productRepo->find(intval($reqProductID));
$color = $colorRepo->find(intval($reqColorID));
for ($i = 0; $i < count($reqSize); $i++) {
$size = $sizeRepo->find(intval($reqSize[$i]));
$variant = $variantRepo->findOneBy(['object' => $product, 'size' => $size, 'color' => $color]);
$quantity = intval($reqQuantity[$i]);
if (null === $variant) {
throw new ItemResolvingException('Selected item is out of stock.');
}
if (null !== $product && null !== $color && null !== $size && null !== $variant) {
// Make a cart item
$item = $this->get('sylius.factory.cart_item')->createNew();
$item->setSize($size);
$item->setVariant($variant);
$item->setQuantity($quantity);
$context = ['quantity' => $quantity];
if (null !== $customer = $cart->getCustomer()) {
$context['groups'] = $customer->getGroups()->toArray();
}
$item->setUnitPrice($priceCalculator->calculate($variant, $context));
// Check for equal products
foreach ($currentCart->getItems() as $cartItem) {
if ($cartItem->equals($item)) {
$quantity += $cartItem->getQuantity();
break;
}
}
if (!$availabilityChecker->isStockSufficient($variant, $quantity)) {
throw new ItemResolvingException('Selected item is out of stock.');
}
$event = new CartItemEvent($cart, $item);
// Update models
$eventDispatcher->dispatch(SyliusCartEvents::ITEM_ADD_INITIALIZE, $event);
$eventDispatcher->dispatch(SyliusCartEvents::CART_CHANGE, new GenericEvent($cart));
$eventDispatcher->dispatch(SyliusCartEvents::CART_SAVE_INITIALIZE, $event);
// Write flash message
$eventDispatcher->dispatch(SyliusCartEvents::ITEM_ADD_COMPLETED, new FlashEvent());
}
}
return $this->redirectAfterAdd($configuration);
}
} catch (ItemResolvingException $exception) {
// Write flash message
$eventDispatcher->dispatch(SyliusCartEvents::ITEM_ADD_ERROR, new FlashEvent($exception->getMessage()));
return $this->redirectAfterAdd($configuration);
}
But when we add one to the cart, the cart total stays 0,00
Are we missing something in order to have the correct totals when adding a CartItem without an ItemResolver?
Thanks in advance.

Yes, with use of this service we were able to correctly modify the quantity and the unit prices. Thanks, we clearly overlooked this service. Our modification of the CartItem ($item->setQuantity($quantity);) we also deleted as this was a custom method, the setter was normally not available.

As specified by Coudenysj you probably need to call the OrderItemQuantityModifier somewhere in your code.
I think you need to manually call the modifier before you set the quantity
$this->get('sylius.order_item_quantity_modifier')->modify($item, $quantity);
// $item->setQuantity($quantity);
Actually it appears you just need to call the modify function and it updates the quantity to the correct value
I had a lot of trouble making my own add to cart code for this reason. I ended up modifying the CartItemType. But now i do need to be able to add multiple to the cart at once. So i would be very interested to hear if this solves your issues.
Please post an update

Take a look at the sylius.order_item_quantity_modifier service: http://docs.sylius.org/en/latest/bundles/SyliusOrderBundle/services.html#orderitemquantitymodifier.

Related

Magento 2 Bundle price

I would like to ask you for some help or even just a comparison.
On my site I have bundles that are dimanically priced with select products, some with radio and some with default.
Magento 2 however shows two different prices between listing and product page. I have checked at code level and on the product page there is a manipulation via js that changes the price. In fact when the page loads you see a price before the update. Have you ever implemented some solution to make the same prices appear? This is the piece of code I am trying to implement but without success as it returns the same price because the products are set to default even though they are not selected on the product page.
$product = $this->productRepository->get($product_sku);
$selectionCollection = $product->getTypeInstance(true)
->getSelectionsCollection(
$product->getTypeInstance(true)->getOptionsIds($product),
$product
);
$children_ids = $this->bundleType->getChildrenIds($product->getId(), true);
$bundle_items = [];
$optionsCollection = $product->getTypeInstance(true)
->getOptionsCollection($product);
foreach ($optionsCollection as $options) {
$optionArray[$options->getOptionId()]['option_title'] = $options->getDefaultTitle();
$optionArray[$options->getOptionId()]['option_type'] = $options->getType();
if ($options->getType() !== 'checkbox' && $options->getRequired() === "1") {
foreach ($selectionCollection as $selection) {
foreach ($children_ids as $bundle_id) {
if ((array_values($bundle_id)[0] === $selection->getEntityId())
&& $options->getId() === $selection->getOptionId()) {
$price = $selection->getPrice();
$qty = $selection->getSelectionQty();
$bundle_items[] = $price * $qty;
}
}
}
}
}
$finale_price = array_sum($bundle_items);
I finally solved it by creating a function that would return the tier price. Mine is a bit more customised because I have different prices depending also on the customer group. I'll leave the code.
public function getBundlePrice($product_sku, $product_sal)
{
$product = $this->productRepository->get($product_sku);
$selectionCollection = $product->getTypeInstance(true)
->getSelectionsCollection(
$product->getTypeInstance(true)->getOptionsIds($product),
$product
);
$children_ids = $this->bundleType->getChildrenIds($product->getId(), true);
$bundle_items = [];
$optionsCollection = $product->getTypeInstance(true)
->getOptionsCollection($product);
foreach ($optionsCollection as $options) {
$optionArray[$options->getOptionId()]['option_title'] = $options->getDefaultTitle();
$optionArray[$options->getOptionId()]['option_type'] = $options->getType();
if ($options->getType() !== 'checkbox' && $options->getRequired() === "1") {
foreach ($selectionCollection as $selection) {
foreach ($children_ids as $bundle_id) {
if ((array_values($bundle_id)[0] === $selection->getEntityId())
&& $options->getId() === $selection->getOptionId()) {
$price = $selection->getPrice();
$qty = $selection->getSelectionQty();
if ($qty > 1 || $selection->getTierPrice()) {
$price = $this->getCustomPrice($selection);
} else if ($this->customerSession->getCustomer()->getGroupId() === "2") {
$price = $selection->getPartnerPrice();
}
$bundle_items[] = $price * $qty;
}
}
}
}
}
return $this->calculator->getAmount(array_sum($bundle_items), $product_sal);
}

Get data from child product

I have a module that observes the status and writes a value into the database.
When the product is simple, it works. But when it is configurable, it writes the value of the Pattern Product and not of the Child.
if (($status !== $previousStatus) && ($status == Mage_Sales_Model_Order::STATE_PROCESSING)) {
$order = $observer->getEvent()->getOrder();
$items = $order->getAllVisibleItems();
mysql_connect('localhost', 'user', 'pass');
foreach($items as $item){
$cd = $item->getProduct()->getData('cd');
mysql_query("INSERT INTO `bd`.`table` (`id`, `cd`) VALUES (NULL, '$cd')");
}
return true;
}
How to identify if it is a configurable product, and get data from the child product?
Magento already has a connection initialised, you don't need to spin up another one directly.
You shouldn't have to specify the database you're connecting to directly.
A configurable product can contain a simple product, to access this you'll need to get the childItem.
The preferred approach for this kind of thing would be to store the products data against the order item, so that if a product is deleted etc in the future you have a full history because order items are decoupled from their products data at time of placing. Read more here: https://magento.stackexchange.com/questions/62111/what-is-the-effect-of-deleting-a-product-with-associated-orders/62142#62142
Just a FYI but getAllVisibleItems returns configurables, if you wanted to see configurables and their simples all in one collection you could use getAllItems instead
if (($status !== $previousStatus) && ($status == Mage_Sales_Model_Order::STATE_PROCESSING)) {
/** #var Mage_Sales_Model_Order $order */
$order = $observer->getData('order');
$items = $order->getAllVisibleItems();
/** #var Mage_Sales_Model_Order_Item $item */
foreach($items as $item){
$cd = $item->getProduct()->getData('cd');
//If configurable get the first child item and the cd value from it
if ($item->getProductType() === Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
$childrenItems = $item->getChildrenItems();
$childItem = reset($childrenItems);
if ($childItem instanceof Mage_Sales_Model_Order_Item) {
$cd = $childItem->getProduct()->getData('cd');
}
}
/** #var Mage_Core_Model_Resource $resource */
$resource = Mage::getSingleton('core/resource');
$writeConnection = $resource->getConnection('core_write');
$query = "INSERT INTO `table` (`cd`) VALUES ('{$cd}')";
$writeConnection->query($query);
}
return true;
}
Ignoring your SQL connection - you should revise that - take a look here:http://fishpig.co.uk/magento/tutorials/direct-sql-queries/
To get the chosen child product of a configurable product, do this;
foreach($items as $item){
// Establish product type and if configurable, load the chosen product
if ($item->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
$simple = reset($item->getChildren());
$_product = $simple->getProduct();
} else {
$_product = $item->getProduct();
}
$cd = $_product->getData('cd');
mysql_query("INSERT INTO `bd`.`table` (`id`, `cd`) VALUES (NULL, '$cd')");
}

Programmatically stop decreasing stock qty while placing an order in magento and increaging qty when order state is Compleate

In Magento 1.9.0.1 by default, it decreases stock qty while placing an order. I want to stop decreasing stock qty while placing an order for the purpose of fake order. And I also want the functionality of increasing stock qty while order state is complete, not pending, not processing. How to do it programmatically or by setting up in Admin panel. If any one knows about it, please reply back.
Thank You
Ankan
Just simply open your admin panel, go to System->Configuration->Catalog Tab->Inventory Then click on Stack Option and change Decrease Stock When Order is Placed to NO.
Thanks,
Lovekesh
In Admin panel, System->Configuration->Catalog Tab->Inventory Then click on Stack Option and change Decrease Stock When Order is Placed to NO. So that it stops the stock qty increasing.In Model/Automatically/Complete/Order/Observer.php page, class Webspidy_Customoption_Model_Automatically_Complete_Order_Observer
{ public function __construct(){}
public function automaticallycompleteorder($observer)
{
//Mage::log('Ankan');
$order = $observer->getEvent()->getOrder();
/$orders = Mage::getModel('sales/order_invoice')->getCollection()
->addAttributeToFilter('order_id', array('eq'=>$order->getId()));/
/*if ((int)$orders->count() !== 0) {
return $this;
}*/
//Mage::log($orderstate);
//if($order->getState() == 'complete'){
if(($order->getState() == 'processing') || ($order->getState() == 'pending_payment')){
if($order->hasInvoices()){
//Mage::log($orderstate);
//Mage::log('Ankan');
//Mage::log($order->getData());
//====== Order Details ================
$orderNumber = $order->getIncrementId(); //Mage::log($orderNumber);
$orderDet = Mage::getModel('sales/order')->load($orderNumber, 'increment_id');
//Mage::log($orderDet->getData());
$orderItems = $orderDet->getItemsCollection()
->addAttributeToSelect('*')
->load();
foreach($orderItems as $orderItem){
$productId = $orderItem->getProductId();
$productOptionSku = $orderItem->getSku();
$productQty = $orderItem->getQtyOrdered();
$product = Mage::getModel('catalog/product')->load($productId);
$sku = $product->getSku();
$centralqty = $product->getStockItem()->getQty();
$values = array();
foreach ($product->getOptions() as $o) {
$p = $o->getValues();
}
foreach($p as $v)
{
$optionSku = $v->getSku();
$optionItem = $v->getQty();
//Mage::log($optionItem);
//Mage::log($productOptionSku.".....".$sku."-".$optionSku.".....".$optionItem);
if($productOptionSku == ($sku."-".$optionSku)){
if($centralqty >= ($optionItem*(int)$productQty)){
//$stockQty = (($centralqty-($optionItem*(int)$productQty))+$productQty);
$stockQty = ($centralqty-($optionItem*(int)$productQty));
}
}
}
$product->save();
//Mage::log($stockQty);
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId);
//$stockItem = Mage::getModel('cataloginventory/stock_item')->load($productId);
$stockItemId = $stockItem->getId();
$stockItem->setData('qty', $stockQty);//(integer)$XMLproduct->QtyInStock
$stockItem->setData('manage_stock',1);
$stockItem->save();
}
}
//======== End ======
}
}
}
And in etc/config.xml,
<global><events>
<sales_order_save_after><observers>
<webspidy_customoption_automatically_complete_order_observer>
<type>singleton</type> <class>Webspidy_Customoption_Model_Automatically_Complete_Order_Observer</class>
<method>automaticallycompleteorder</method>
</webspidy_customoption_automatically_complete_order_observer>
</observers></sales_order_save_after></events></global> After a long process, I have got a success by creating Observer in my custom module.
I am using Magento 1.9
I have tried with the following code. it's OK But i have another smart code
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($productId);
$stockItemId = $stockItem->getId();
$stockItem->setData('qty', $stockQty);
$stockItem->setData('manage_stock',1);
$stockItem->save();
My code is following:-
Mage::getModel('cataloginventory/stock')->backItemQty($productId,$new_qty);

Codeignighter Cart class base fee

I have a unique shop where some products incur a base fee, lets say
A photographer charges $20 for the first hour then $1 there-after.
I am passing a variable into my codeignighter cart ; so for 5 hours I would pass the variable into cart->insert();
$item['id'] = 1;
$item['qty'] = 5;
$item['base'] = 20.00;
I made some changes to the cart class so this would work and has been fine so far, what I now need and cant seem to figure this out is when there are options it considers it a different product and this fee is charged once per rowid.
I would like my class to only allow 1 charge for the item regardless of the various options.
Below are the three functions I created inside my Cart class
and I call set_base($item) in the _save_cart() function.
private function set_base($item)
{
if( $this->base_exist($item) )
{
return FALSE;
}
// Only allow the base cost for 1 row id, it doesnt matter which one, just one
$this->_base_indexes['rowid'][$item['id']] = $item['rowid'];
$this->_cart_contents['cart_total'] += $item['base'];
return TRUE;
}
private function base_exist($item)
{
if ( array_key_exists($item['id'] , $this->_base_indexes['applied']) )
{
if ( ( $item['rowid'] == $this->_base_indexes['applied'][$item['id']] ) )
{
return TRUE;
}
}
return FALSE;
}
private function base_reset()
{
$this->_base_indexes = array();
$this->_base_indexes['applied'] = array();
return $this->_base_indexes;
}
Inside _save_cart();
I call
$this->base_reset();
Inside the cart_contents() loop I have added;
if(isset($val['base']))
{
$this->set_base($val);
}
$this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']);
Hope this was clear :/
OK i changed it a little bit,
the foreach loop in the save_cart function now looks like; and I can remove the three functions I had before.
foreach ($this->_cart_contents as $key => $val)
{
// We make sure the array contains the proper indexes
if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
{
continue;
}
if(isset($val['base']))
{
//If it doesnt exist, add the fee
if (!(isset($this->_base_indexes[$val['id']]) == $val['rowid']) )
{
$this->_base_indexes[$val['id']] = $val['rowid'];
$this->_cart_contents['cart_total'] += $val['base'];
$sub = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']) + $val['base'];
}
else
{
//$this->_cart_contents[$key]['base'] = 0;
$sub = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
}
}
$this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']);
$this->_cart_contents['total_items'] += $val['qty'];
$this->_cart_contents[$key]['subtotal'] = $sub;
}

Add new values to a attribute option in Magento

I am trying to add new values to an attribute option in Magento using a script to speed up the process since I have over 2,000 manufacturers.
Here is a piece of code that I used to perform exactly this task. Create a custom module (using ModuleCreator as a tool) and then create a mysql4-install-0.1.0.php under the sql/modulename_setup folder. It should contain the following (adapted to your own data, of course!).
$installer = new Mage_Eav_Model_Entity_Setup('core_setup');
$installer->startSetup();
$aManufacturers = array('Sony','Philips','Samsung','LG','Panasonic','Fujitsu','Daewoo','Grundig','Hitachi','JVC','Pioneer','Teac','Bose','Toshiba','Denon','Onkyo','Sharp','Yamaha','Jamo');
$iProductEntityTypeId = Mage::getModel('catalog/product')->getResource()->getTypeId();
$aOption = array();
$aOption['attribute_id'] = $installer->getAttributeId($iProductEntityTypeId, 'manufacturer');
for($iCount=0;$iCount<sizeof($aManufacturers);$iCount++){
$aOption['value']['option'.$iCount][0] = $aManufacturers[$iCount];
}
$installer->addAttributeOption($aOption);
$installer->endSetup();
More documentation on the Magento wiki if you want.
If you don't want to do it in a custom module, you could just create a php file that starts with:
require_once 'app/Mage.php';
umask(0);
Mage::app('default');
Answer of Jonathan is correct. But if you want to perform it without installer i.e in any other code, then you might find this helpful:
$arg_attribute = 'manufacturer';
$manufacturers = array('Sony','Philips','Samsung','LG','Panasonic','Fujitsu','Daewoo','Grundig','Hitachi','JVC','Pioneer','Teac','Bose','Toshiba','Denon','Onkyo','Sharp','Yamaha','Jamo');
$attr_model = Mage::getModel('catalog/resource_eav_attribute');
$attr = $attr_model->loadByCode('catalog_product', $arg_attribute);
$attr_id = $attr->getAttributeId();
$option['attribute_id'] = $attr_id;
foreach ($manufacturers as $key=>$manufacturer) {
$option['value'][$key.'_'.$manufacturer][0] = $manufacturer;
}
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$setup->addAttributeOption($option);
More information can be found here.
I have created a function to dynamically add option to attribute
public function addAttributeOptions($attributeCode, $argValue)
{
$attribute = Mage::getModel('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
if ($attribute->usesSource()) {
$id = $attribute->getSource()->getOptionId($argValue);
if ($id)
return $id;
}
$value = array('value' => array(
'option' => array(
ucfirst($argValue),
ucfirst($argValue)
)
)
);
$attribute->setData('option', $value);
$attribute->save();
//return newly created option id
$attribute = Mage::getModel('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
if ($attribute->usesSource()) {
return $attribute->getSource()->getOptionId($argValue);
}
}
You can add an option to your attribute by providing code and option value
$this->addAttributeOptions('unfiorm_type', 'leotartd')
Important! (Hopefully this helps somebody, cause I was stuck like 2h with this issue)
If you are using special characters (such as ä, ö, ü, ß, ×, ...) make sure to encode them properly!
array_walk($manufacturers , create_function('&$val', '$val = utf8_encode($val);'));
Here a simple and very fast way....
Delete an option
/* My option value */
$value = 'A value';
/* Delete an option */
$options = array();
$entity_type_id = Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId(); // Product Entity Type ID
$attribute = Mage::getModel('eav/entity_attribute')->loadByCode($entity_type_id, $attribute_code); // Load attribute by code
if ($attribute && $attribute->usesSource()) {
$option_id = $attribute->getSource()->getOptionId($value); // Check Option ID from value...
if ($option_id) {
$options['delete'][$option_id] = true;
$attribute->setOption($options)->save();
}
}
/* END ! */
Add or update an option
/* Add/Update an option */
$options = array();
$entity_type_id = Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId(); // Product Entity Type ID
$attribute = Mage::getModel('eav/entity_attribute')->loadByCode($entity_type_id, $attribute_code); // Load attribute by code
if ($attribute && $attribute->usesSource()) {
$option_id = $attribute->getSource()->getOptionId($value); // Check Option ID...
$options['order'][$option_id] = 10; // Can be removed... Set option order...
$options['value'][$option_id] = array(
0 => $value, // Admin Store - Required !
1 => $value, // Store id 1 - If U want ! Can be removed
);
$attribute->setDefault(array($option_id)); /* If you want set option as default value */
$attribute->setOption($options)->save(); /* That's all */
}
/* END ! */
In my tutorial I am explaining how to read the options from the CSV and create the options pro grammatically.
http://www.pearlbells.co.uk/add-attribute-options-magento-scripts/
please click the tutorial for further explanation
function createAttribute($options) {
$option = array('attribute_id' =>
Mage::getModel('eav/entity_attribute')->getIdByCode(
Mage_Catalog_Model_Product::ENTITY,
'color'
)
);
for ($i = 0; $i < count($options); $i++) {
$option['value']['option'.$i][0] = $options[ $i ]; // Store View
$option['value']['option'.$i][1] = $options[ $i ]; // Default store view
$option['order']['option'.$i] = $i; // Sort Order
}
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$setup->addAttributeOption($option);
}
Answer of Arvind Bhardwaj enter code here is correct. But if you want to perform it without installer i.e in any other code, then you might find this helpful:
Agree with Arvind but it's only works for insert the single option value and if you want to perform insert multiple option value then you just needs to replace the code from "$option['value'][$key.''.$manufacturer] = $manufacturer;" to "$option['values'][$key.''.$manufacturer] = $manufacturer;" to this.
below is the final code
require_once 'app/Mage.php';
umask(0);
Mage::app('default');
$arg_attribute = 'manufacturer';
$manufacturers = array('Sony', 'Philips', 'Samsung', 'LG', 'Panasonic', 'Fujitsu', 'Daewoo', 'Grundig', 'Hitachi', 'JVC', 'Pioneer', 'Teac', 'Bose', 'Toshiba', 'Denon', 'Onkyo', 'Sharp', 'Yamaha', 'Jamo');
$attr_model = Mage::getModel('catalog/resource_eav_attribute');
$attr = $attr_model->loadByCode('catalog_product', $arg_attribute);
$attr_id = $attr->getAttributeId();
$option['attribute_id'] = $attr_id;
foreach ($manufacturers as $key => $manufacturer) {
$option['values'][$key . '_' . $manufacturer] = $manufacturer;
}
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$setup->addAttributeOption($option);
I hope its works for insertion multiple option.

Categories