Cakephp 3: view.ctp sum two variables - sum

I have a view that shows an associated array of Revenues. I have made a collection in the controller to isolate two variables that I need to add together and display as currency.
public function view($id = null)
{
$annualOperatingBudget = $this->AnnualOperatingBudgets->get($id, [
'contain' => ['Azinstitutions', 'BudgetExpenses', 'BudgetExpenses.ExpenseTitles', 'BudgetRevenues', 'BudgetRevenues.RevenueTitles']
]);
$collection = new Collection($annualOperatingBudget->budget_revenues);
$revenuesGroup1 = $collection->match(['revenue_title.revenue_group' => 1 ]);
$revenuesGroup2 = $collection->match(['revenue_title.revenue_group' => 2 ]);
$tuitionAndFees = $collection->match(['revenue_title.revenue_title' => 'Tuition and Fees']);
$lessScholarshipAllowance = $collection->match(['revenue_title.revenue_title' => '- less Scholarship Allowance']);
$this->set(compact('annualOperatingBudget', $annualOperatingBudget,'revenuesGroup1', 'revenuesGroup2', 'tuitionAndFees', 'lessScholarshipAllowance'));
}
I am able to see the variables with the debug kit:
annualOperatingBudget (array)
revenuesGroup1 (array)
revenuesGroup2 (array)
tuitionAndFees (array)
4 (App\Model\Entity\BudgetRevenue)
id 5
annual_operating_budget_id 1
revenue 1278
revenue_title_id 5
revenue_title (array)
lessScholarshipAllowance (array)
5 (App\Model\Entity\BudgetRevenue)
id 6
annual_operating_budget_id 1
revenue -257
revenue_title_id 6
revenue_title (array)
I would like to add the two 'revenue' s together
I tried:
<?= $this->Number->currency(
($tuitionAndFees->revenue) + ($lessScholarShipAllowance->revenue),
'USD', ['places' => 1])
?>
But I get several errors:
Notice (8): Undefined property: Cake\Collection\Iterator\FilterIterator::$revenue [ROOT\plugins\Twit\src\Template\AnnualOperatingBudgets\view.ctp, line 49]
Notice (8): Undefined variable: lessScholarShipAllowance [ROOT\plugins\Twit\src\Template\AnnualOperatingBudgets\view.ctp, line 49]
Notice (8): Trying to get property of non-object [ROOT\plugins\Twit\src\Template\AnnualOperatingBudgets\view.ctp, line 49]

You have to iterate the $tuitionAndFees and the $lessScholarShipAllowance before trying to get the revenue property. Something like this:
foreach($tuitionAndFees as $tuitionAndFee){
echo $tuitionAndFee->revenue
}

If all you need in the view is the total of all tuition and fees, you can use
$tuitionAndFees = $collection
->match(['revenue_title.revenue_title' => 'Tuition and Fees'])
->sumOf('revenue');
This will return just the sum of the matched items. Do something similar for $lessScholarShipAllowance, and then in your view, simply
$this->Number->currency($tuitionAndFees + $lessScholarShipAllowance,
'USD', ['places' => 1])

Related

getProduct()->getTag() return null, when it should return tags associated to the Product

In my project, we have products that has tag called serviceItem. Those item with that tag when ordered should be separated by the quantity into individuals order.
It issue is that getTags() returns null, and getTagIds gets "Call to a member function getTagIds() on null" when it gets to the next loop.
Is there a reason for why getTags() returns null?
private function transformOrderLines(OrderEntity $order): array
{
/**
* TODO: If we need to send advanced prices,
* the price value of the the lines array should be changed to caldulate the advanced price,
* with the built in quantity calculator
*/
$lines = [];
foreach ($order->getLineItems() as $orderLine) {
$hasDsmServiceItemTag = $orderLine->getProduct()->getTags();
$lines[] = [
'name' => $orderLine->getLabel(),
'sku' => substr($orderLine->getProduct()->getProductNumber(), 0, 19),
'price' => (string) ($orderLine->getProduct()->getPrice()->first()->getNet()
* $order->getCurrencyFactor()), //gets original price, calculates factor
'quantity' => (string) $orderLine->getQuantity()
];
}
$shipping = $this->transformShipping($order);
if ($shipping) {
$lines = array_merge($lines, $shipping);
}
return $lines;
}`
I also tried $orderLine->getProduct()->getTags()->getName() it also return "Call to a member function getTags() on null"
The problem is wherever the $order is fetched from the DB the orderLineItem.product.tag association is not included in the criteria.
For performance reasons shopware does not lazily load all association when you access them on entities, but you have to exactly define which associations should be included when you fetch the entities from the database.
For the full explanation take a look at the docs.

Property "CDbCriteria.:centerId" is not defined in yii

I am using select method in yii it gives error "Property "CDbCriteria.:centerId" is not defined"
if (0 < self::model()->countByAttributes(
'centerId = :centerId AND qTypeId = :qTypeId',
array(
':centerId' => $centerId,
':qTypeId' => $qTypeId,
)
)) {
throw new Exception('Duplicate Entry for center and que type');
}
You're using this method in a wrong way. You skipped first argument, which should be list of active record arguments used as filter (see documentation). You probably need something like:
if (0 < self::model()->countByAttributes([
'centerId' => $centerId,
'qTypeId' => $qTypeId,
]) {
throw new Exception('Duplicate Entry for center and que type');
}
Or use count():
if (0 < self::model()->count(
'centerId = :centerId AND qTypeId = :qTypeId',
[
':centerId' => $centerId,
':qTypeId' => $qTypeId,
]
)) {
throw new Exception('Duplicate Entry for center and que type');
}

How to set a maximum stock level for total WooCommerce variations sold, disregarding individual variation stock levels

I'm wondering if we are able to hook into WooCommerce to set a maximum amount of stock that can be purchased for a variable product. Disregarding the individual variation stock levels once this maximum amount is reached.
For example, I have a variable product selling workshop groups. There are 4 variations, each with a stock level set at 100. This is because no group can have more than 100 people in. However, only 250 tickets are available for sale (not 400 that we might expect because of the 4x100 quantity).
So this works as far as the max 100 places per workshop group. We just need to somehow be able to limit the total stock level of all 4 variations to 250.
I had hoped enabling the parent product "Manage stock" option and setting this to 250 would work. But obviously, variations must override this. If we can hook into that and turn that back on even when variation stock management is in use that might be a nice way of solving this.
Thanks for any help.
I came up with a solution to my problem by doing the following:
Add 2 custom fields to the WooCommerce product page, which will store the max quantity of the total variations we can sell and also the max quantity of an individual variation. The code for this is:
// Modify WooCommerce Product Settings
add_action('woocommerce_product_options_inventory_product_data', 'wc_add_custom_field' );
function wc_add_custom_field() {
$fields = array('Total quantity' => 'total_quantity','Variation quantity' => 'variation_quantity');
$field_description = array('total_quantity' ='description','variation_quantity' ='description');
$field_placeholder = array('total_quantity' =>'e.g. 300','variation_quantity' =>'e.g. 100');
foreach ($fields as $key => $value) {
woocommerce_wp_text_input( array(
'id' => $value,
'label' => $key,
'description' => $field_description[$value],
'desc_tip' => 'true',
'placeholder' => $field_placeholder[$value]
) );
}
}
// Save Fields
add_action( 'save_post_product', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields_save( $post_id ){
update_post_meta( $post_id, 'total_quantity', $_POST['total_quantity'] );
update_post_meta( $post_id, 'variation_quantity', $_POST['variation_quantity'] );
}
Add cart/basket validation rules to stop customers being able to purchase products that exceed the value of the custom "total_quantity" field added above:
add_action( "woocommerce_add_to_cart_validation","sc_woocommerce_add_to_cart_validation", 1, 5 );
function sc_woocommerce_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id, $variations ) {
// Iterate through each variation and get the total stock remaining
$product_variable = new WC_Product_Variable($product_id);
$product_variations = $product_variable->get_available_variations();
settype($variation_stock_availability, "integer");
foreach ($product_variations as $variation) {
$variation_stock_availability = +(int)$variation['max_qty'];
}
$count_variations = count($product_variations);
$total_quantity = get_post_meta( $product_id, 'total_quantity', true );
$variation_quantity = get_post_meta( $product_id, 'variation_quantity', true );
// formula to test if any stock remaining based on sold variations
$formula = $count_variations * $variation_quantity;
$formula1 = (int)$formula + (int)$quantity;
$formula1 = $formula1 - $variation_stock_availability;
// Iterating through each cart item and use the current running quantity in the cart in the forumula
foreach (WC()->cart->get_cart() as $cart_item_key=>$cart_item ){
// count(selected category) quantity
$running_qty += (int) $cart_item['quantity'];
$formula2 = (int)$formula + (int)$running_qty;
$formula2 = $formula2 - $variation_stock_availability;
// More than allowed products in the cart is not allowed
if ($formula2 >= $total_places) {
wc_add_notice( sprintf( __( "Unfortunately there is no availability based on your selection", "donaheys" )), 'error' );
$passed = false;
return $passed;
}
}
// More than allowed products in the cart is not allowed
if ($formula1 >= $total_places) {
// Add the error
wc_add_notice( sprintf( __( "Unfortunately there is no availability based on your selection", "donaheys" )), 'error' );
$passed = false;
return $passed;
} else {
$passed = true;
return $passed;
}
$running_qty = 0;
}
The result of the above code ensures we are can set a maximum amount of stock that can be purchased for a variable product, disregarding the individual variation stock levels once this maximum amount is reached.

PayPal Api Patch on Itemlist doesn't work

I want to add an item to my transaction.
$json = '
[
{
"name": "Voucher",
"description":"Voucher",
"price":"50.00",
"currency":"EUR",
"quantity":"1"
}
]';
$patchAddItem = new \PayPal\Api\Patch();
$patchAddItem->setOp('add')
->setPath('/transactions/0/item_list/items')
->setValue(json_decode($json));
$patchReplace = new \PayPal\Api\Patch();
$patchReplace->setOp('replace')
->setPath('/transactions/0/amount')
->setValue(json_decode('{
"total": "159.00",
"currency": "EUR",
}'));
$patchRequest = new \PayPal\Api\PatchRequest();
$patchRequest->setPatches(array($patchAddItem, $patchReplace));
try {
$this->payment->update($patchRequest, $this->apiContext);
} catch (PayPal\Exception\PayPalConnectionExceptio $ex) {
echo '<pre>';print_r(json_decode($ex->getData()));exit;
}
But I get following Error
Eception: Got Http response code 400 when accessing https://api.sandbox.paypal.com/v1/payments/payment/PAY... in PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Core/PayPalHttpConnection.php on line 154
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Transport/PayPalRestCall.php on line 73: PayPal\Core\PayPalHttpConnection->execute("[{"op":"add","path":"/transactions/0/item_list/ite"... )
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Common/PayPalResourceModel.php on line 102: PayPal\Transport\PayPalRestCall->execute(array[1],"/v1/payments/payment/PAY-1S151200BX2478240LEAG3CI","PATCH","[{"op":"add","path":"/transactions/0/item_list/ite"... ,null)
PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Api/Payment.php on line 615: PayPal\Common\PayPalResourceModel::executeCall("/v1/payments/payment/PAY-1S151200BX2478240LEAG3CI","PATCH","[{"op":"add","path":"/transactions/0/item_list/ite"... ,null,object,null)
At this moment I didn't execute the payment object. Do I have to edit the total attribut from amount too? Well, I tried this too, with same issue...
Even if you are sending only one item to PayPal you still have to set them as an item list with setItemList().
That array should be visible if you json_decode in your payment array:
[item_list] => Array
(
[items] => Array
(
[0] => Array
(
[name] => Ground Coffee 40 oz
[sku] => 123123
[price] => 52.80
[currency] => USD
[quantity] => 1
)
)
I had not run a patch for an item yet. I attempted to send an 'add' similar to your code and tried changing the path to '/transactions/0/item_list/items/1' using the next number in the items array. But could not get an add to work.
The only way I could modify the item_list was to do a complete 'replace' of the item_list, so in a running shopping cart would have to include all the items being purchased, not just the new item.
To do this I prefer to use the functions from the PayPal sdk vs building the json arrays. Their examples of how to create and execute a payment are fairly good and use the SDK functions. http://paypal.github.io/PayPal-PHP-SDK/sample/
However the example on updating a payments builds the json arrays outright.
Below is a testing function to modify the item_list using the Paypay PHP SDK Class Functions. I hard coded the Subtotal and Total to match the values coming form the shopping cart plus the increase from the new item. The item_list is also hard coded using PP's example data. Otherwise item's arrays would be built off of a user's shopping cart items. The type is set to 'replace'.
So, yes. Subtotals and Totals need to be updated to match as well, else the PP call will fail.
function updatePayPalPayment ($type, $createdPayment, $total, $subtotal, $shipping, $currency) {
$subtotal = '54.80';
$total = '71.73';
$details = new Details();
$details->setShipping($shipping)
->setSubtotal($subtotal);
$amount = new Amount();
$amount->setCurrency($currency)
->setTotal($total)
->setDetails($details);
$item1 = new Item();
$item1->setName('Ground Coffee 40 oz')
->setCurrency('USD')
->setQuantity(1)
->setSku("123123") // Similar to `item_number` in Classic API
->setPrice(52.80);
$item2 = new Item();
$item2->setName('Granola bars')
->setCurrency('USD')
->setQuantity(1)
->setSku("321321") // Similar to `item_number` in Classic API
->setPrice(2.0);
$itemList = new ItemList();
$itemList->setItems(array($item1, $item2));
$patchItem = new Patch();
$patchItem->setOp($type)
->setPath('/transactions/0/item_list')
->setValue($itemList);
$patchAmount = new Patch();
$patchAmount->setOp($type)
->setPath('/transactions/0/amount')
->setValue($amount);
$patchRequest = new PatchRequest();
$patchRequest->setPatches(array($patchAmount, $patchItem));
$update = $createdPayment->update($patchRequest, getApiContext());
return $update;
}
I also have found it very helpful to set the apiContext for logging to DEBUG and output to a file in development for much better error messages.
'log.LogEnabled' => true,
'log.FileName' => '_PayPal.log',
'log.LogLevel' => 'DEBUG',
Hope that helps.

How to use Yii CArrayDataProvider

I am new to php and Yii and need some help regarding showing array in the webpage.
In the controller I open my e-mail inbox and iterate through the e-mails in inbox and build array with each e-mail address as key having values
if (array_key_exists($fromemail,$senders))
{ $senders[$fromemail]['rcount']++; }
else {
$senders[$fromemail]['e-mail'] = $fromemail;
$senders[$fromemail]['Name'] = $fromname;
$senders[$fromemail]['rcount'] = 1;
}
$model->top_senders = $senders;
$this->render('Step2',array('model'=>$model,));
Then in the view file of Step2 I want to show the data in CGridview
if (isset($model->top_senders))
{
$gridDataProvider = new CArrayDataProvider($model->top_senders);
$gridDataProvider->setData($model->top_senders);
$gridColumns = array(
array('name'=>'e-mail', 'header'=>'E-mail','value' =>'$data->e-mail'),
array('name'=>'rcount', 'header'=>'# of mails','value'=>'$data->rcount'),);
$this->widget('bootstrap.widgets.TbGridView',array('dataProvider' => $gridDataProvider,'template' => "{items}",'columns'=>$gridColumns));
}
But I will get error during rendering of the table: PHP notice Undefined offset: 0
/**
125 * Renders a data cell.
126 * #param integer $row the row number (zero-based)
127 */
128 public function renderDataCell($row)
129 {
130 $data=$this->grid->dataProvider->data[$row];
What I am doing wrong? Can anyone help me?
You should't add data to provider as follows:
$gridDataProvider->setData($model->top_senders);
It added during initialization. You must be sure that the array has a key id, otherwise you need to specify it manually (it must be unique) as follows:
$gridDataProvider = new CArrayDataProvider($model->top_senders, array(
'id'=>'Name',
));
You will also need to make sure that the array $model->top_senders has the following structure:
array(
'0'=>array(...user data here),
'1'=>array(...user data here),
...
);
If you var_dump($gridDataProvider->data) you'd notice there's no value for the 0th index in the array. This happens when you run some filter function on the array. Assuming there are 5 values in the filtered array, the filtered array would look something like this when var_dump'd.
array(5) {
[1] => Object (Mailer) {…},
[2] => Object (Mailer) {…},
[4] => Object (Mailer) {…},
[8] => Object (Mailer) {…},
[9] => Object (Mailer) {…},
}
A filter operation on an array can leave the array looking like the above.
When the CGridView is trying to feed the view with data, it does it sequentially—this, I find, is a shortcoming in Yii and should be raised as an issue.
In order to fix this, use PHP's array_values() like so…
$properly_indexed_array = array_values($filtered_array);
This will copy the values of the filtered array into a new array. This is not the optimal solution in terms of memory. So far, I do not see any means in PHP other than this though.
You may then go ahead and set this as the data for your data provider like so…
$gridDataProvider->setData($properly_indexed_array);