I would like to programmatically reorder specific orders by cronjob and send an order confirmation by mail. I have a simple php file in my root directory which is triggered by a cronjob every once in a while:
<?php
include_once 'app/Mage.php';
Mage::app();
//some existing order ids
$orderIds= array('911', '1106', '926');
foreach($orderIds as $orderId){
Mage::unregister('rule_data');
Mage::getModel('adminhtml/session_quote')
->clear();
/* #var Mage_Sales_Model_Order $order */
$order = Mage::getModel('sales/order')->load($orderId)
->setReordered(true);
/* #var Mage_Sales_Model_Quote $quote */
$quote = Mage::getModel('sales/quote')
->setStoreId($order->getStoreId())
->assignCustomer(Mage::getModel('customer/customer')->load($order->getCustomerId()))
->setUseOldShippingMethod(true);
/* #var Mage_Adminhtml_Model_Sales_Order_Create $model */
$model = Mage::getModel('adminhtml/sales_order_create')
->initFromOrder($order)
->setQuote($quote);
/* #var Mage_Sales_Model_Order $newOrder */
$newOrder = $model->createOrder();
$newOrder->setQuoteId($quote->getId())
->sendNewOrderEmail();
$model->getSession()
->clear();
}
It seems to work so far, new orders are placed and the emails are sent. But the problem I experienced is that the customer in the new orders is always the one from the first old order (in this case the order with the id 911). Of course, this also affects the order confirmation emails, so they're all sent to the same email address... Also, the order items seem to add up in the cart, so the last order which is reordered contains all the order items of the previous orders... What am I doing wrong?
I appreciate every help I can get! Thank you!
There are two problems:
You are calling Mage::getModel('adminhtml/session_quote')->clear(),
which gets a new instance of the session and tries to clear that.
But the Mage_Adminhtml_Model_Sales_Order_Create class gets the
session by using Mage::getSingleton('adminhtml/session_quote'),
which always gets the same instance from the Magento registry. So
you are trying to clear another session instance.
Even if you would try to clear the right session instance from the
Magento registry by using:
Mage::getSingleton('adminhtml/session_quote')->clear(), there would
still be a problem. As the Mage_Adminhtml_Model_Session_Quote class
doesn't define this method, it ultimately calls
Varien_Object->unsetData(), which only does this: $this->_data = array();
But the information in the Mage_Adminhtml_Model_Session_Quote class
is stored in the $_quote, $_customer, $_store and $_order properties,
so they don't actually get cleared, but persist.
I solved the problem by deleting the Mage_Adminhtml_Model_Session_Quote instance in Magento registry before each of the orders, so that it needs to create a new one for each order.
Just add this at the beginning of the loop:
Mage::unregister('_singleton/adminhtml/session_quote');
Cheers! ;)
Related
I wanted to get data from a bdd, like I always do I create a method for add specific condition to my request, but you'll see after I tried many things else.
Here the request:
/**
* #return CongesEchangeRtt[] Returns an array of CongesEchangeRtt objects
*/
public function findByDate($agents, $date) {
return $this->createQueryBuilder('c')
->andWhere('c.eLogin IN (:val)')
->andWhere('c.eDateJour LIKE :date')
->setParameter('val', $agents)
->setParameter('date', $date . "%")
->getQuery()
->getResult()
;
}
This request had to return me every data that match with condition, right?
But we're wrong, lol, IDK why but this is not what it do, in fact I tried it, in pur SQL, in "phpMyAdmin" and it worked so I think that my problem come from symfony, anyway this request return me only one object per user (agents). Datas I get are correct, but I don't get every data that match for each user... I lost my mind here in first, but it's only the beginning.
After that I tried a simple "findBy" method with the user array in parameter, I got every date of every user in my array, but it's to mutch, I want to get only thoses with the correct dates, I mean those who are in the 'actual' month that I display in the front, 'actual' is relative.
So I generate an array with every date of the month and tried this:
findBy(["login" => $myUserArray, "dates" => $myArrayOfDates]);
You now what I got? Something weird! Right!
Duplicate content... But some that are not in the bdd, I mean, here an example:
I have many data/lines in this table that look like this:
login|date(vacation)|presence|absence|comment
so:
"xxxx.xxxx"|"2021-05-14"|etc
"xxxx.xxxxx"|"2021-05-24"|etc
And I got this in my PHP:
"xxxx.xxxx"|"2021-05-14"|etc
"xxxx.xxxx"|"2021-05-14"|etc
I have the right number of data, but they're all the same...
The problem was caused by the difference between my entity and the Table in the bdd.
'Cause this is a part of an old system, and idk why but the dev how code it add two PRIMARY KEY to this table, and when I generate my entity with symfony he didn't created it as the table was. he forgot one PRIMARY KEY, maybe beaucause it's a special case, normaly i read that a table only need one PRIMARY KEY, anyway, i fixed it by adding the right annotation to my paramater that represent the field in the database that was the second PRIMARY KEY, after that the problem was solved.
I change this :
/**
* #var \Datetime
*
* #ORM\Column(name="e_date_jour", type="date", nullable=false, options={"default"="0000-00-00"})
*/
private $eDateJour = '0000-00-00';
To this :
/**
* #var string
*
* #ORM\Column(name="e_date_jour", type="string", nullable=false, options={"default"="0000-00-00"})
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $eDateJour = '0000-00-00';
I change the get and set fonction too, 'cause symfony specified the return's type by himself when you generate the entity, it's a good practice btw.
I'm new here. Thanks in advance for your advice.
I’m working on an app which will ask the user how many items they made.
The user will enter a number. My app should then create that many new records in a table called 'Items_Made'.
E.g. The app asks “How many items did you make?”, the user enters “19”, the app then creates 19 new records in the 'Items_Made' table.
I've managed to pull together some code (shown below) that creates ONE new record, but I would like it to create several. I probably need some kind of loop or 'while' function but am unsure how to do so.
var ceateDatasource = app.datasources.Items_Made.modes.create;
var newItem = ceateDatasource.item;
ceateDatasource.createItem();
This code successfully creates 1 record. I would like it to be able to create several.
Creating a lot of records via client script is not recommended, especially if you loose connection or the app gets closed by mistake. In my opinion, the best way to handle this would be via server script for two things: First, It's more reliable and second, it's faster. As in the example from the official documentation, to create a record you need to do something like this:
// Assume a model called "Fruits" with a string field called "Name".
var newRecord = app.models.Fruits.newRecord();
newRecord.Name = "Kiwi"; // properties/fields can be read and written.
app.saveRecords([newRecord]); // save changes to database.
The example above is a clear example on how to create only one record. To create several records at once, you can use a for statement like this:
function createRecordsInBulk(){
var newRecords = [];
for(var i=0; i<19; i++){
var newRecord = app.models.Fruits.newRecord();
newRecord.Name = "Kiwi " + i;
newRecords.push(newRecord);
}
app.saveRecords(newRecords);
}
In the example above, you initiate newRecords, an empty array that will be responsible for holding all the new records to create at once. Then using a for statement, you generate 19 new records and push them into the newRecords. Finally, once the loop is finished, you save all the records at once by using app.saveRecords and passing the newRecords array as an argument.
Now, all this is happening on the server side. Obviously you need a way to call this from the client side. For that, you need to use the google.script.run method. So from the client side you need to do the following:
google.script.run.withSuccessHandler(function(result) {
app.datasources.Fruits.load();
}).createRecordsInBulk();
All this information is clearly documented on the app maker official documentation site. I strongly suggest you to always check there first as I believe you can get a faster resolution by reading the documentation.
I'd suggest making a dropdown or textbox where the user can select/enter the number of items they want to create and then attach the following code to your 'Create' button:
var createDatasource = app.datasources.Items_Made.modes.create;
var userinput = Number(widget.root.descendants.YourTextboxOrDropdown.value);
for (var i = 0; i <= userinput; i++) {
var newItem = createDatasource.item;
createDatasource.createItem();
}
Simple loop with your user input should get this accomplished.
I have an event object with Payments collection. When event is cancelled I need to add those payments to the appropriate User object Refunds collection. Based on the documentation I came to the following schematic script:
_(this.Payments).forEach(function(payment) {
var user = LoadDocument(payment.UserId);
user.Refunds.push(new { EventId = this.Id, Payment = payment });
}
There are two things in this schematic script that I didn't find how to do right in the documentation:
1. Load another document by Id (line 2)
2. Create new json-object (line 3)
The LoadDocument() is correct, however the loaded document isn't automatically tracked by any Unit of Work when loaded within a Patch.
You have to tell Raven to update/store that document as well:
var user = LoadDocument(payment.UserId);
user.Refunds.push({ EventId = this.Id, Payment = payment });
PutDocument(user.UserId, user);
If you really want to do this from a patch, the above might work. However, this seems like a more domain specific operation and might be better to model the behaviour in your application code (i.e. raise an event and add the refunds to the user objects from code). Not 100% sure how Raven handles transactions within patches and so on...
Edit: For your second question: You don't need to use the 'new' keyword
I have written one extension for making service order.
The issue I am facing here is,
There are FE users belong to three FE user groups namely "client", "Admin" and "Employee".
Here the client can make order and he should be able to see only his orders.
And the admin can see all orders done by different clients.
And the employee should be able to see only some clients orders.
Currently I made a order table with N:1 relation with FE user table. So every order should relate with any one client.
So in controller, I am checking the login user and using custom query in repository, I am accessing order related to loggedin client (FE user)
In file OrdersController.php
public function listAction() {
$orders = $this->ordersRepository->orderForLoginUsr();
$this->view->assign('orders', $orders);
}
In file OrdersRepository.php
public function orderForLoginUsr(){
$loggedInUserId = $GLOBALS ['TSFE']->fe_user->user['uid'];
$query = $this->createQuery();
$query->matching(
$query->equals('user', $loggedInUserId)
);
$query->setOrderings(array('crdate' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
return $query->execute();
}
But here my question is how to make admin user able to see all the orders done by all clients?
I have to write different template and action that calling function findAll() ?
$orders = $this->ordersRepository->findAll();
And how to set for usergroup Employee ?
Thanks in Advance
I think that the easiest way is to actually implement 3 actions with 3 different plugins, something like: listClientAction, listAdminAction and listEmployeeAction
In each of those action, you implement a method in your repository that fetch the right list of order with the good ordering:
orderForLoginClient(), orderForLoginEmployee(), orderForLoginAdmin()
What does the trick actually is that there will be 3 plugins on your page, one for each action. In each instance of your plugin, you set the access for the right be_group.
Don't forget to add the actions and plugin in the localconf and ext_table files.
I hope it will help!
Olivier
If your view is almost the same for client, admin, employee you should simply add a method like getOrderWithPermissionsForUser($currentUser);
In the method itself you should check for the usergroup and call different queries on your Repo.
If your view is different from usergroup to usergroup, you should use different templates with partials for the same parts.
If the data of the views is the same, just change the template for each usergroup in the action. If not use different actions.
Here is a helper method for easily changing your templatefile.
/**
* This method can change the used template file in an action method.
*
* #param string $templateName Something like "List" or "Foldername/Actionname".
* #param string $templateExtension Default is "html", but for other output types this may be changed as well.
* #param string $controllerName Optionally uses another subfolder of the Templates/ directory
* By default, the current controller name is used. Example value: "JobOffer"
* #param \TYPO3\CMS\Fluid\View\AbstractTemplateView $viewObject The view to set this template to. Default is $this->view
*/
protected function changeTemplateFile($templateName, $templateExtension = 'html', $controllerName = null, AbstractTemplateView $viewObject = null)
{
if (is_null($viewObject)) {
$viewObject = $this->view;
}
if (is_null($controllerName)) {
$controllerName = $this->getControllerContext()->getRequest()->getControllerName();
}
$templatePathAndFilename = $this->getTemplateRootpathForView($controllerName . '/' . $templateName . '.' . $templateExtension);
$viewObject->setTemplatePathAndFilename($templatePathAndFilename);
}
I've recently added Facebook authentication to my site, which results in the user automatically being directed to the Address page once they have authenticated. Those users logging in this way will not be shown the extra questions I had asked them during the normal checkout/registration process ('How did you hear about us ?', etc..).
My question is, how do I add these fields into the Address form and get them to be inserted into the ps_customer table as they would be if a user was registering using the traditional way ? I could add fields to the Address template/controller right now, but they would end up being saved in the ps_address table instead
Any help would be much appreciated,
Thanks
It is possible but will need some work around.
After you added the fields to the address form, you have to overried the Address.php class. Do it as followed.
1) In override/classes folder find the Address.php file, and also open classes/Address.php file .
2) In file classes/Address.php file, find add function which will be as below
/**
* #see ObjectModel::add()
*/
public function add($autodate = true, $null_values = false)
{
if (!parent::add($autodate, $null_values))
return false;
if (Validate::isUnsignedId($this->id_customer))
Customer::resetAddressCache($this->id_customer);
return true;
}
Copy that exact function to override/classess/Address.php file and place it in the class.
3) Just after the below code
if (!parent::add($autodate, $null_values))
return false;
Write down your code to insert the extra fields to another table. Just get the POSTed values and write you won sql query to insert it into customers table or any other tables if you need.
Thank you