Adding a ubercart line item and updating order total - drupal-7

I don't know why this is so complicated but all I want to do is to add a line item in my order when user changes payment method in Ubercart. I managed to do this by adding the following line in my payment method.
uc_order_line_item_add($order->order_id, 'Pay at door','Pay at door',5);
This seems to add the line item but the order total does not get updated. When I refresh the page, I can see the line item being added there. Obviously, I don't want to refresh the screen and I want the new order total and the line item to be displayed during callback of my payment method. I guess this is achieved by calling jquery but I could not find anything useful in Google.
Can someone help?

Hope this answer isn't too late for you, but have you tried the following:
$order->line_items[] = uc_order_line_item_add($order->order_id, 'Pay at door','Pay at door',5);
The issue is that uc_order_line_item_add simply updates the database not the current order object that is being passed around and is later used to calculate totals etc, thankfully uc_order_line_item_add returns a line item array that can simply be appended to the order object. I am not sure that it will work inside your payment method but it works for me in hook_uc_order, where I had the similar issue of the tax not being updated until a second refresh.
In case it helps my full example code is below:
<?php
function my_module_uc_order($op, &$order, $arg2) {
switch ($op) {
case 'presave':
// using presave as taxes are calculated during save
$line_item_id = FALSE;
if (!is_array($order->line_items)) {
$order->line_items = array();
}
else {
foreach ($order->line_items as $index => $line_item) {
if ($line_item['type'] == 'my_line_item') {
$line_item_id = $line_item['line_item_id'];
break;
}
}
}
if ($line_item_id === FALSE) {
// Add item.
// Dummy amount for testing.
$amount = 3;
// uc_order_line_item_add returns a line_item array so we can just add it to the order object.
$order->line_items[] = uc_order_line_item_add($order->order_id, 'my_line_item', 'My Item Title (added)', $amount);
// uc_order_line_item_add($order_id, $type, $title, $amount, $weight = NULL, $data = NULL).
}
else {
// Update item.
// Dummy amount for testing.
$amount = 4;
// uc_order_update_line_item($id, $title, $amount, $data = NULL).
uc_order_update_line_item($line_item_id, 'My Item Title (updated)', $amount);
// Manually modify amount.
$order->line_items[$index]['amount'] = $amount;
}
break;
}
}
/**
* Implements hook_uc_line_item().
*/
function my_module_uc_line_item() {
$items[] = array(
'id' => 'my_line_item',
'title' => t('Custom text'),
'weight' => 0,
'default' => FALSE,
'stored' => TRUE,
'add_list' => TRUE,
'calculated' => TRUE,
);
return $items;
}

Related

Arraylist doesn't get refilled and/or filtered

I have a list in angular, an array. OnInit it gets filled from the right corresponding database items. I created a form above it. When you enter something in the form, it acts like a filter. This works, the first time. When you erase something from the form and enter something else, the list should be refreshed and afterwards filtered based on the new input. This doesn't happen. I put the formula that happens on onInit in my function to refill the list.
Below you can find my function (I left the console logs in) and a screenshot of the problem. First I look for a user (joeri.boons#hi10.be) which returns three results. Than I erase the user and look based on a month 7. The screen returns a new unfilterd list while in the console it still holds the list of 3 x user joeri.boons#hi10.be. So there is an inconsistency to. If you look at screen result you would think of a filter problem, the console points at a refreshproblem.
if more code is required let me know.
updateWithFilter(): void {
console.log("function update filter reached")
console.log(this.listadapted);
if(this.listadapted == true){
// this.timesheetsHandled = {} as TimeSheet[];
this.getHandledSheet();
console.log("getHandledSheet executed")
}
if(this.filterUsername.trim() && !this.filterYear && !this.filterMonth){
console.log("option 1 reached")
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
this.timesheetsHandled = this.timesheetsHandled.filter(sheet => sheet.username == this.filterUsername);
this.listadapted = true;
} else if(!this.filterUsername.trim() && !this.filterYear && this.filterMonth){
console.log("option 2 reached");
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
console.log("before filter");
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
this.timesheetsHandled = this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth);
console.log("after filter");
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
// console.log(this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth));
this.listadapted = true;
} else if .. more options
}
ngOnInit(): void {
this.getHandledSheet();
}
getHandledSheet(): void {
this.timesheetService.getAllTimesheets().subscribe({next: (response: TimeSheet[]) => {this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED') }}) ;
}
My guess would be that this is caused by loading data in ngOnInit. As the documentation (https://angular.io/api/core/OnInit) states : [...] It is invoked only once when the directive is instantiated.
I suspect that you create one instance and re-use it and the ngOnInit method does not get called again.
UPDATE:
The issue is that the call to this.getHandledSheet(); does a call to .. .subscribe({next: .. which is delayed and the rest of the function is executed first.
So the actual code after next: is only executed after the timeSheetService is done loading the data.
So either you apply the filter in the
{next: (response: TimeSheet[]) => {this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED') }}
block after filtering for 'HANDLED' or you'll try to await in the update function.
Create two variables, one that will always remain unfiltered, then another that will be filtered.
The problem will be that the original list is filtered, hence you are losing the original data after filtering!
timesheetHandled: TimeSheet[];
timesheetHandledOriginal: TimeSheet[];
updateWithFilter(): void {
console.log('function update filter reached');
console.log(this.listadapted);
if (this.listadapted == true) {
// this.timesheetsHandled = {} as TimeSheet[];
this.getHandledSheet();
console.log('getHandledSheet executed');
}
if (this.filterUsername.trim() && !this.filterYear && !this.filterMonth) {
console.log('option 1 reached');
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
this.timesheetsHandled = this.timesheetHandledOriginal.filter(
sheet => sheet.username == this.filterUsername
);
this.listadapted = true;
} else if (!this.filterUsername.trim() && !this.filterYear && this.filterMonth) {
console.log('option 2 reached');
console.log(this.filterUsername.trim());
console.log(this.filterYear);
console.log(this.filterMonth);
console.log('before filter');
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
this.timesheetsHandled = this.timesheetHandledOriginal.filter(
sheet => sheet.month == this.filterMonth
);
console.log('after filter');
this.timesheetsHandled.forEach(sheet => console.log(sheet.username));
// console.log(this.timesheetsHandled.filter(sheet => sheet.month == this.filterMonth));
this.listadapted = true;
}
// else if .. more options
}
ngOnInit(): void {
this.getHandledSheet();
}
getHandledSheet(): void {
this.timesheetService.getAllTimesheets().subscribe({
next: (response: TimeSheet[]) => {
this.timesheetsHandled = response.filter(sheet => sheet.status == 'HANDLED');
this.timesheetHandledOriginal = JSON.parse(JSON.stringify(this.timesheetsHandled));
},
});
}

Protractor - How do I return an element by the text contained within a child element?

was wondering if I could get some help on this. I have an array of <tr> elements returned when I declare this webelement reference
locationAuditsBySourceListViewTableRows : { //returns an array
get : function(){
return this.locationAuditsBySourceListViewTable.all(by.repeater('audit in location.displayedProfileAudits'));
}
},
Within one of those <tr> elements is a span that contains the text that specifies the row I would like to select.
The current framework I am working within has some similar methods, which I tried to refactor for my purposes, but this code is failing:
locationAuditsListViewRowBySource : {
value : function(sourceName){
return this.locationAuditsBySourceListViewTableRows.filter(elem => {
//elem - placeholder as we iterate through each element within the locationAuditsBySourceListViewTableRows array
return elem.element(by.css(".source-name.ng-binding")).getText().then((val) => {
return val.toLowerCase() == sourceName.toLowerCase();
});
}).first();
}
},
My feeling is that the way in which I try to reference the span within the table row is incorrect (call is made within the .filter() method):
return elem.element(by.css(".source-name.ng-binding")).getText().then((val) => {
Here is the step definition itself:
Then(/^I should see that location audit for source row: "([^"]*)" has a label for the source$/, function (sourceName, callback) {
browser.wait(EC.visibilityOf(listingsPageObj.locationAuditsBySourceListViewContainer), timeouts.EC_TIMEOUT).then(() => {
browser.wait(EC.visibilityOf(listingsPageObj.locationAuditsBySourceListViewTable), timeouts.EC_TIMEOUT).then(() => {
browser.wait(() => {
return listingsPageObj.locationAuditsBySourceListViewTableHeaders.count().then(cnt => (cnt > 0)); //which means that there are audit results displayed
}).then(() => {
//find a row in the list of displayed audits
var tableRow = listingsPageObj.locationAuditsListViewRowBySource(sourceName);
});
});
});
});
The usage of elem.element(by.css(".source-name.ng-binding")).getText() is right.
But some small changes in code is better:
return val.trim().toLowerCase() == sourceName.trim().toLowerCase();
return listingsPageObj.locationAuditsBySourceListViewTableHeaders
.count().then(cnt => {return cnt > 0;}) // I think you need return

How to ensure only one record is active

I want that when a record is saved and marked as active, all other records are marked INactive.
I've tried the following code in my model:
public function beforeSave($options = array()) {
if (!empty($this->data['Ticket']['is_active'])) {
$this->data['Ticket']['is_active'] = 0;
}
return true;
}
However this code is error
Use afterSave
Instead of using beforeSave, it's more appropriate to use afterSave, and updateAll like so:
public function afterSave($created) {
if (!empty($this->data[$this->alias]['is_active'])) {
$this->updateAll(
array('is_active' => 0),
array(
'id !=' => $this->id,
'is_active' => 1
)
);
}
}
I.e. after successfully saving a record, if it is active disable all the others.
Note: be sure to use the same method signature as the parent class. It varies depending on which version of CakePHP you are using.
You can write before save method like
public function beforeSave($options=array()){
if (!empty($this->data[$this->alias]['is_active'])) {
$this->data[$this->alias]['is_active'] = 0;
}
return true;
}

How to define a checkbox variable in Cakephp

I have a form in my Plugin elements and what i would like to insert the checkbox value into a table named it_queries and field status_type and its giving me an error Undefined variable: variableValue [APP\Plugin\Feedback\View\Elements\comment_add.ctp, line 37] .I have declared the variable in my controller like this
$this->set('variableValueStatus', 'Pending');
and this is line 37 thats giving me the error
Below is the Controller code
App::uses('FeedbackAppController', 'Feedback.Controller');
class CommentsController extends FeedbackAppController
{
public $components = array('Feedback.Comments');
public function add($foreign_model = null, $foreign_id = null)
{
if (empty($foreign_model) ||
empty($foreign_id) ||
!$this->request->is('post')
)
{
foreach ($_POST['likebutton'] as $pageId => $likeFlag) {
$dbFlag = $likeFlag ? 'Yes' : 'No';
}
return $this->redirect('/');
}
App::uses($foreign_model, 'Model');
$Model = ClassRegistry::init($foreign_model);
if (!($Model instanceof Model))
{
return $this->redirect('/');
}
if ($Model->hasAny(array($Model->primaryKey => $foreign_id)) == false)
{
return $this->redirect('/');
}
if (!isset($this->request->data['Comment']['foreign_model']) ||
!isset($this->request->data['Comment']['foreign_id']) ||
$this->request->data['Comment']['foreign_model'] != $foreign_model ||
$this->request->data['Comment']['foreign_id'] != $foreign_id)
{
return $this->redirect('/');
}
$user_id = null;
if (isset($this->Auth))
{
$user_id = $this->Auth->user('id');
}
$this->request->data['Comment']['foreign_model'] = $Model->name;
$this->request->data['Comment']['foreign_id'] = $foreign_id;
$this->request->data['Comment']['user_id'] = $user_id;
$this->Comment->create();
if (!$this->Comment->save($this->request->data))
{
$this->set('validation_errors', $this->Comment->validationErrors);
return;
}
$this->redirect($this->request->referer().'#comment-'.$this->Comment->id);
}
}
and in the add view in my element here is how i am trying to accessing the variable value
echo $this->Form->checkbox('ItQuery.status_type', array('type' => 'hidden', 'value'=>$variableValueStatus));
If someone can show me how to fix this, that would be awesome
You are only passing the variable down on !post and if foreign model and foreign id are not set.
That will most likely not work in most of the cases.
You should be always passing down a variable if you do not check on it in the view prior to using it.
But it would still be wrong, anyway. You would have to use "default" instead of "value" if you dont want your form to fall back on the old value on POST (which it shouldnt).
Also, it is always better to use $this->request->data to properly fill the form with defaults:
if (!$this->request->is('post')) {
$this->request->data['ItQuery']['status_type'] = 'Pending';
}
See working-with-forms

CakePHP 2.2.4: Redirect not Working When Using Component

It has come to my attention that the redirect controller method is not working part of the time. No message appears when I set debug > 0. I don't echo any code before calling the redirect method so it shouldn't be because of "headers already sent".
Let's take a look at my ArticlesController add action where redirect works in one instance but not in another.
public function add($page = null) {
// Custom component to get if user has required access level
// of page to write an article. If not, setflash to an error message
// specific to user's access level and redirect.
$access_message = $this->CustomPage->AccessMessage(4, $this->viewVars['access']);
if($access_message){
// Flash works but redirect does not
$this->Session->setFlash(__($access_message));
$this->redirect(array('action' => 'index', 'page' => $page));
// Also tried
// $this->redirect(array('controller'=>'articles', 'action' => 'index', 'page' => $page), null, true);
} else
{
if ($this->request->is('post')) {
$this->Article->create();
if ($this->Article->save($this->request->data)) {
// BLAH BLAH save post, do other stuff
// BLAH BLAH save post, do other stuff
// This flash and redirect works
$this->Session->setFlash(__('The article has been saved'));
$this->redirect(array('action' => 'view', 'id' => $article_id, 'page' => $page));
} else {
$this->Session->setFlash(__('The article could not be saved. Please, try again.'));
} // end else if article cannot be saved
} // if method is post
} // end if user has access
} // end add action
It definitely has something to do with the component but I'm not sure what. Maybe since redirect is called right after the component is used, the "$this" is trying to do the redirect method on the component instead of the controller. I tried $this->Article->redirect and reloading the Article model before the redirect but neither of those worked.
My component code is:
public function AccessMessage($required_level, $user_level) {
if(!$user_level && $this->_View->viewVars['access']){
$user_level = $this->_View->viewVars['access'];
}
if(!$required_level || !$user_level || $user_level != $required_level){
$accessModel = ClassRegistry::init('Access');
$access_message = $accessModel->field('access_message', array('Access.id' => $required_level));
}
return $access_message;
}
Edit 1: Ok so I did some digging to find exactly where the problem is stemming from. The USE of the component is not the problem which I thought it was before. If all I have in my component is
public function AccessMessage($required_level, $user_level) {
if(!$user_level && $this->_View->viewVars['access']){
$user_level = $this->_View->viewVars['access'];
}
if(!$required_level || !$user_level || $user_level != $required_level){
$access_message = 1;
}
return $access_message;
}
Then it works. The issue is with these two lines which are correctly implemented because they return the value for $access_message I am expecting, but they are interfering with the ability to redirect. Perhaps headers are already sent out?
$accessModel = ClassRegistry::init('Access');
$access_message = $accessModel->field('access_message', array('Access.id' => $required_level));
Please note I have also tried:
$access_message = ClassRegistry::init('Access')->field('access_message', array('Access.id' => $required_level));
And
$this->loadModel('Access');
$access_message = $this->Access->field('access_message', array('Access.id' => $required_level));
Gists:
component gist: https://gist.github.com/970a951715205c222348
controller gist: https://gist.github.com/2b90e5af2518a81672fb
access model gist: https://gist.github.com/bhndbrwneyes/f333a93f0a21302d832f
You may have space before/after php Opening/Closing tags in controller and models. Remove all the closing tags from all controllers and models and any whitespace before opening tags. Then check the result.
Not an answer yet, but at least there are some variables in your code that may not be defined, or errors that can occur:
public function AccessMessage($required_level, $user_level) {
if(!$user_level && $this->_View->viewVars['access']){
$user_level = $this->_View->viewVars['access'];
}
if(!$required_level || !$user_level || $user_level != $required_level){
$access_message = 1;
}
return $access_message;
}
Variable $access_message will only be defined if a user is not allowed to access the page
An error may occur if the 'access' viewVar is not set at all
Change it to this:
public function AccessMessage($required_level, $user_level) {
$access_message = 0;
if(!$user_level && $this->_View->get('access')){
$user_level = $this->_View->get('access');
}
if(!$required_level || !$user_level || $user_level != $required_level){
$access_message = 1;
}
return $access_message;
}
[updated] saw you did have the $access_message defined on your gist (https://gist.github.com/970a951715205c222348)
However:
This will not work
App::uses('Component', 'Controller', 'ClassRegistry', 'Utility');
App::uses() takes two arguments; the 'class' you would like to use and the location it can be found. The line above should be written as:
App::uses('Component', 'Controller/Component');
App::uses('Controller', 'Controller');
App::uses('ClassRegistry', 'Utility');
But I wonder if ClassRegistry needs to be loaded manually
[update 2] You really have a lot 'weird' things going on in your application, so I wonder if we'll be able to sort that out:
public function add($page = null) {
$access = $this->viewVars['access'];
if($this->CustomPage->AccessMessage(4, $access)){
$this->Session->setFlash(__($this->CustomPage->AccessMessage(4, $access)));
$this->redirect(array('action' => 'index', 'page' => $page));
}
// ......
}
Where is 'viewVars['access']' set?
You're passing 'viewVars['access']' as the second parameter ($user_level) to AccessMessage(), but inside AccessMessage() you're trying to use the same viewVar again if the parameter '$user_level' was not set?
$this->CustomPage->AccessMessage() is called twice once to check if it returned anything, then to use it. Not very efficient
.
public function add($page = null) {
// Where does is $this->viewVars['access'] come from? Where is it set?
$access = empty($this->viewVars['access'])? null : $this->viewVars['access'];
$message = $this->CustomPage->AccessMessage(4, $access);
if ($message) {
$this->Session->setFlash(__($message));
$this->redirect(array('action' => 'index', 'page' => $page));
}
// ......
}
On a further note. you're redirecting the user only if a 'message' was found and not empty, NOT based on the current users permissions, you might consider splitting the two;
In your component:
public function HasAccessLevel($required_level, $user_level) {
if(!$user_level || $user_level != $required_level){
return false;
}
return true;
}
public function AccessMessage($required_level) {
return ClassRegistry::init('Access')->field('access_message', array('Access.id' => $required_level));
}
In your controller:
public function add($page = null) {
// Where does is $this->viewVars['access'] come from? Where is it set?
$access = empty($this->viewVars['access'])? null : $this->viewVars['access'];
if($this->CustomPage->HasAccessLevel(4, $access)){
$this->Session->setFlash(__($this->CustomPage->AccessMessage(4)));
$this->redirect(array('action' => 'index', 'page' => $page));
}
// ......
}
Cake 2: At the top of the component: add
public function initialize(Controller $controller) {
$this->Controller = $controller;
}
inside the function you can redirect like so:
$this->Controller->redirect(array('plugin' => false, 'controller' => 'users', 'action' => 'index'));
If the redirect is called, but you are not redirected I guess you have some permission check in your index() action that prevents the access. Can you confirm or post the whole controller code?

Resources