VtigerCRM: How can I avoid modifying entity data? - php

I want to avoid modifing every data from a SalesOrder when the status equals "Cancelled", I get it to work with the part that involves SalesOrder info data, but I can`t get it to work with product part... any ideas?
$pedido = SalesOrder_Record_Model::getInstanceById($entityId);
$vtEntityDelta = new VTEntityDelta ();
$anterior = $vtEntityDelta->getOldValue($entity->getModuleName(), $entityId, "sostatus");
$actual = $pedido->get("sostatus");
$VTIGER_BULK_SAVE_MODE = true;
if($anterior == "Approved" || $anterior == "Cancelled" || (($actual == "Approved" || $actual == "Cancelled") && (!$vtEntityDelta->hasChanged($entity->getModuleName(), $entityId, 'sostatus')))){
foreach ($pedido->getModule()->getFields() as $field)
if($vtEntityDelta->hasChanged($entity->getModuleName(), $entityId, $field->getName()))
$pedido->set($field->getName(), $vtEntityDelta->getOldValue($entity->getModuleName(), $entityId, $field->getName()));
$pedido->set("mode","edit");
$pedido->save();
}
That works for info data as I was saying, some has any idea about this??
Thanks!

Related

Import from CSV, record already exist else successful, else failed

I have a script that imports records to multiple database tables, all relation to one parent entity (address).
If the address already exist, extra columns should just be updated according to the csv. The same counts for all other entities. A counter ($exist++) should increase or an array ($existingRecords) should be filled with already existing records.
If mandatory fields are empty in the record, that record should be added to another array ($failedRecords) or another counter ($failed++) should increase.
If the address doesn't yet exist and should be created with all fields just a counter ($successful++) should increase.
In the end I have an array $result that give the number of failed, successful, and already existing (but updated) records, for user feedback.
How can I implement this in a nice clean manner without messing my current script too much up? Because what is happening right now, if a record already exist the $exist counter increase but the $successful counter as well, and I only want the $exist counter to increase if a record already already exists, and only the $successful counter should increase if a record still has to be added and was added successfully. Same goes for the $failed counter.
Here is my script (with what I tried):
public function import(CsvFile $csv) {
$root = __DIR__.'/../../../../../';
$file = $root.'var/data/'.$csv->getCsvName();
$fp = fopen($file, "r");
$batchSize = 25;
$header = null;
$successful = 0;
$failed = 0;
$exist = 0;
$results = [];
while ($row = fgetcsv($fp, null, ";")) {
if ($header === null) {
$header = $row;
continue;
}
$record = array_combine($header, $row);
// cast all values to correct data types
foreach ($record as $key => &$value) {
if (strpos($key, 'datum') !== false ||
strpos($key, 'tijdstip') !== false &&
strlen($value) == 8 &&
is_numeric($value)
) {
$value = \DateTime::createFromFormat('Ymd', $value);
}
if ($value === "") {
$value = null;
}
if (is_numeric($value)) {
intval($value) == $value ? $value = (int)$value : $value = (float)$value;
}
}
// required fields
if (!$record['name'] ||
!$record['surname'] ||
!$record['email'] ||
!$record['phone'] ||
!$record['street'] ||
!$record['houseNo'] ||
!$record['town'] ||
!$record['postcode'] ||
!$record['location'] ||
!$record['lecture'] ||
!$record['session'] ||
) {
$failed++;
continue;
}
$student = $this->em->getRepository(Student::class)->findStudent(
$record['name'], $record['surname'],
$record['email'], $record['phone']
);
if (!$student) {
$student = new Student();
$student->setName($record['name']);
$student->setSurname($record['surname']);
$student->setEmail($record['email']);
$student->setPhone($record['phone']);
} else {
$exist++;
}
$student->setAge($record['age']);
$student->setLength($record['length']);
$address = $this->em->getRepository(Address::class)->findOneBy([
'street' => $record['street'],
'houseNo' => $record['houseNo'],
'town' => $record['town'],
'postcode' => $record['postcode'],
);
if (!$address) {
$address = new Address();
$address->setStreet($record['street']);
$address->setHouseNo($record['houseNo']);
$address->setPostcode($record['postcode']);
$address->setTown($record['town']);
}
$student->setAddress($address);
$lecture = $this->em->getRepository(Lecture::class)->findOneBy([
'location' => $record['location'],
'lecture' => $record['lecture'],
'session' => $record['session'],
]);
if (!$lecture) {
$lecture = new Lecture();
$lecture->setLocation($record['location']);
$lecture->setLecture($record['lecture']);
$lecture->setSession($record['session']);
}
$lecture->setTime($record['time']);
$lecture->setSubject($record['subject']);
$student->setLecture($lecture);
$validationErrors = $this->validator->validate($student);
if (!count($validationErrors)) {
$this->em->persist($student);
$successful++;
}
if (($successful % $batchSize) == 0) {
$this->em->flush();
}
}
fclose($fp);
$csv->setImported(true);
$this->em->persist($csv);
$this->em->flush(); // Also persist objects that did not make up an entire batch
$results['successful'] = $successful;
$results['failed'] = $failed;
$results['exist'] = $exist;
return $results;
}
You can replace the existing records counter with a flag that is set to TRUE only if the current record is an existing one, and use it at the end before persisting the record.
You can set the flag to FALSE on the beginning of each loop:
while ($row = fgetcsv($fp, null, ";")) {
$existingRecordFlag = FALSE; // initialize the flag for the current record
then update it instead of the counter :
if (!$student) {
$student = new Student();
$student->setName($record['name']);
$student->setSurname($record['surname']);
$student->setEmail($record['email']);
$student->setPhone($record['phone']);
} else {
$existingRecordFlag = TRUE; //update the flag
}
then before persisting user check the value for the flag and according to it update the counter:
$validationErrors = $this->validator->validate($student);
if (!count($validationErrors)) {
$this->em->persist($student);
if ( !$existingRecordFlag){
$successful++; //new record
}else{
$exist++; //existing user
}
}else{
$failed++; //failed as the validations has errors
continue;
}
also you can add the existing records to the batch counter as they might be updated, they can also be persisted with the successful ones
if ((($successful+$exist) % $batchSize) == 0) {
$this->em->flush();
}
In your block handling the addresses you could an existing/failed counter quite easily:
$exist++;
if (!$address) {
$exist--;
$successful++;
//...
}
This will always increase the exist-counter, but jump back if address is empty and increase the new address-counter instead. Alternatively you could work with array_push($record) and array_pop($record) to add/remove the current record from a list. This might cause memory issues, since you keep your records in multiple arrays and you might run into out of memory errors.
For missing mandatory fields you'd have to do a check on the $address afterwards to see if it's not empty/containing invalid data and then increase the counter/update the field.
If you want

OpenCart "order again" functionality in the order history

I am interested in implementing an "order again" function for OpenCart, with a button located in the order history list under account/order. This button would take all this particular order's products (and its associated options) and add them all back to the cart.
The following code I have allows me to loop through my product as well as its respective product options depending on the order ID.
$products = $this->model_account_order->getOrderProducts(
$this->request->get['order_id']
);
foreach ($products as $product) {
$options = $this->model_account_order->getOrderOptions(
$this->request->get['order_id'],
$product['order_product_id']
);
foreach ($product['option'] as $option) {
// Implement custom logic
}
}
I have no issues at the moment to get the products that I have already ordered. What I do not understand what to do and need help on is - how do I use this [if this is the way to add items to cart] to add the items back to the cart?
By knowing the product ID and quantities (and options if any) you can just call:
$this->cart->add($product_id, $quantity, $options); // use array() instead of $options if there are none
for each product respectively. $options is an array, to see what structure it has to have look into the AJAX request when adding a product with options to the cart (I think it's single array with product_option_id as key and option_value as value).
Try this
$this->cart->add($product_id, $quantity, $options);
This is actually already implemented on the customer side of the cart. Here's the code contained in the /catalog/controller/account/order.php that does exactly what you want (1.5.5.1 in this particular case)
if (isset($this->request->get['order_id'])) {
$order_info = $this->model_account_order->getOrder($this->request->get['order_id']);
if ($order_info) {
$order_products = $this->model_account_order->getOrderProducts($this->request->get['order_id']);
foreach ($order_products as $order_product) {
$option_data = array();
$order_options = $this->model_account_order->getOrderOptions($this->request->get['order_id'], $order_product['order_product_id']);
foreach ($order_options as $order_option) {
if ($order_option['type'] == 'select' || $order_option['type'] == 'radio') {
$option_data[$order_option['product_option_id']] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'checkbox') {
$option_data[$order_option['product_option_id']][] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'text' || $order_option['type'] == 'textarea' || $order_option['type'] == 'date' || $order_option['type'] == 'datetime' || $order_option['type'] == 'time') {
$option_data[$order_option['product_option_id']] = $order_option['value'];
} elseif ($order_option['type'] == 'file') {
$option_data[$order_option['product_option_id']] = $this->encryption->encrypt($order_option['value']);
}
}
$this->session->data['success'] = sprintf($this->language->get('text_success'), $this->request->get['order_id']);
$this->cart->add($order_product['product_id'], $order_product['quantity'], $option_data);
}
$this->redirect($this->url->link('checkout/cart'));
}
}
Upon investigation and posting on the OpenCart website, it turns out reorder is already a functionality in OpenCart, but we never enabled it [or someone disabled it months ago]. We had to renable it by adding a button and attributed the reorder link under /catalog/controller/account/order.php.
We also added some custom logic in our:
if ($order_option['type'] == 'select' || $order_option['type'] == 'radio') {
$option_data[$order_option['product_option_id']] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'checkbox') {
$option_data[$order_option['product_option_id']][] = $order_option['product_option_value_id'];
} elseif ($order_option['type'] == 'text' || $order_option['type'] == 'textarea' || $order_option['type'] == 'date' || $order_option['type'] == 'datetime' || $order_option['type'] == 'time') {
$option_data[$order_option['product_option_id']] = $order_option['value'];
} elseif ($order_option['type'] == 'file') {
$option_data[$order_option['product_option_id']] = $this->encryption->encrypt($order_option['value']);
}
Since we have a custom option type called checkbox+quantity. For the future users, please make sure to add your custom option type logic to this logical statement.
Thanks Jay for pointing this out as well.

Loop isn't working for my Twitter API

I have a setup where it favourites and retweets a specific tweet. For some reason, the code works for the favourites however retweets do not. Can anyone see the issue?
$method = 'statuses/retweet/'.$url[3];
$amt = "26";
$sub = rand(1,3);
$amt1 = $amt-$sub;
if($_POST['favorite'] == "true" || $_POST['favorite'] == "1"){
for ($x1=1; $x1<=$amt1; $x1++)
{
$content = $connection[$x1]->post('favorites/create', array('id' => $url[3]));
}
}
if($_POST['retweet'] == "true" || $_POST['retweet'] == "1"){
for ($x2=1; $x2<=$amt; $x2++)
{
$content = twitteroauth_row('statuses/retweet/'.$url[3], $connection[$x2]->post($method), $connection[$x2]->http_code);
}
}
have you tried declaring
$amt = 26
instead of
$amt="26"
EDIT:
for ($x2=1; $x2<=amt; $x2++)
{
$content = twitteroauth_row('statuses/retweet/'.$url[3], $connection[$x2]->post($method), $connection[$x2]->http_code);
}
you have used amt instead of $amt in the loop condition

skipping through foreach with multiple conditions

I'm trying to get my foreach to skip certain children in the xml based on one of the childrens values. The first code works for skipping just 1 but I want to skip a lot. The second code won't skip any of them.
<?php
$gamespage = simplexml_load_file("http://gamepage.com/games?xml=1");
foreach($gamespage->games->game as $game)
{
$gid = $game->appID;
if ($gid != 65920) {
} }
second code (not skipping any):
<?php
$gamespage = simplexml_load_file("http://gamepage.com/games?xml=1");
foreach($gamespage->games->game as $game)
{
$gid = $game->appID;
if ($gid != 65920 || $gid != 40940 || $gid != 50110 || $gid != 8990) {
} }
You need to use && instead of ||
Or even better
if (!in_array($gid, array(65920, 40940, ...)))

What would you change in my code for best practices/maintenance?

I've got a small snippet of code below and I was curious what types of things you would change with regards to best practices/code maintainablity and so on.
function _setAccountStatus($Username, $AccountStatus)
{
if ($Username == '' || ($AccountStatus != 'Active' || $AccountStatus != 'Banned' || $AccountStatus != 'Suspended')) {
// TODO: throw error here.
}
$c1 = new Criteria();
$c1->add(UsersPeer::USERNAME,$Username);
$rs = UsersPeer::doSelect($c1);
if (count($rs) > 0) {
$UserRow = array_pop($rs);
$UserRow->setAccountStatus($AccountStatus);
try {
$UserRow->save();
} catch ( PropelException $e ) {
return false;
}
return true;
}
return false;
}
I would use the empty() instead of $Username == '' in your if statement. I haven't used propel before, but I would prefer to have this method be on my User object itself with the fetching and saving of the user object performed by a seperate object. Pseudo code would be something like this.
$user = userManager->getUser($username);
$user->setAccountStatus($accountStatus);
$userManager->saveUser($user);
An else clause before the last return false would be prefererred, just to make the code more readable.

Categories