I am in the process of creating several WHMCS modules, one of the modules to process an invoice in our own bookkeeping system uses the WHMCS internal API like:
add_hook('InvoicePaid', 1, function ($params)
{
$invoiceid = $params['invoiceid'];
$command = "getinvoice";
$adminuser = "";
$values["invoiceid"] = $invoiceid;
$results = localAPI($command,$values,$adminuser);
if ($results['result']!="success") echo "An Error Occurred: ".$results['result'];
//get netto price and description of first item
$netto = $results['subtotal'];
$desc = $results['items']["item"][0]["description"];
//... other code not needed for question.
}
The module is tested manually by creating an invoice and setting it as paid by an admin user, in this case it works and the price and description are processed.
However, when a normal user pays an invoice, the module gives both $netto and $desc as empty strings.
My suspicions are that this is because of the empty $adminuser parameter, but what value should it be? Do I have to set it to an existing admin username of the system? Or is there a username you can always use?
If it has to be a system admin username, how would other people know that they have to change this before integrating the module?
You do need to have an $adminuser in place. I believe you can almost 90% of the time assume an admin id of 1 would be valid for most setups. In my modules I have a field in the settings where the customer can select which one to use, and it defaults to use a 1 in the event they haven't saved anything yet.
Related
I'm using Memberpress and Memberpress Corporate on my WordPress site and I'm trying to add a custom function when a member signs up under a specific membership type or purchases a specific membership type. When this happens I need to grab the corporate account ID and do something with it.
I'm using the hook mepr-event-transaction-completed as this fires for both recurring and non-recurring transactions, though I also tried mepr-event-non-recurring-transaction-completed just to be sure.
This is my code:
$transaction = $event->get_data();
$membership_type_ids = array(1, 2, 4);
if (in_array($transaction->product_id, $membership_type_ids) && $transaction->txn_type == 'payment') {
$org_id = $transaction->corporate_account_id;
my_custom_function($org_id);
}
When the user is signing up for this membership type with a subscription, this is no problem, I can retrieve this, however if they are signing up with a one-time non-recurring transaction, the corporate account id is returning as 0, even though when I go to check the database, there is a corporate account id there.
Does the corporate account id get set at a different time for non-recurring transactions?
Okay, so after speaking to Memberpress, it turns out this just doesn't get set at the right time.
I used a workaround as so:
if($transaction->corporate_account_id !== "0" && $transaction->corporate_account_id !== 0) {
//some irrelevant code here about what to do if the corporate id actually works
} else {
write_log('sending cron to add new user due to corporate id returning as 0, please check in 2 minutes. tran_num = '.$transaction->trans_num);
wp_schedule_single_event( strtotime("+2 minutes"), 'send_fix_for_zero_transaction', array($transaction),false );
return;
}
add_action( 'send_fix_for_zero_transaction', 'single_transaction_create_corporate' );
function single_transaction_create_corporate($transaction) {
//NOTE: this function is only used it a one-time transaction is created in the backend to create a corporate membership, because there is a bug in memberpress that means the corporate_id isn't sent back by the event. This event will only fire if that is the case, otherwise this is handled by rc_setup_new_org. This should eventually be deprecated when Memberpress fix their issues.
$trans_num = $transaction->trans_num;
$full_trans = MeprTransaction::get_one_by_trans_num($trans_num);
//do whatever you need to do with the transaction here, you have the number now
}
Regarding OpenCart store selling digital downloads.
I am trying to add a script to the product page to warn the customer if that specific product (download) is already purchased and exists in Account>Downloads.
The reason is to avoid customers purchasing the same product twice.
Appreciate your help.
Edit:
I have tried getting the product IDs of all orders by a customer using a SQL query and that works fine externally but inside OpenCart I am facing issues.
A query such as:
SELECT product_id
FROM ocuk_order_product
WHERE order_id IN (
SELECT order_id
FROM ocuk_order
WHERE customer_id = 'xxxx'
)
My main problem is not being sure how to get similar results in OpenCart Product page. (Which pages and path exactly and where inside the files)
Tried this post as well: Opencart get product downloads
But it is not exactly working on product page (product.php)
Opencart Version 3.0.2.0
Ok I'll take a stab at this but let's clarify a few things:
1) DB_PREFIX is simply a php constant that's declared in your config.php file. Based on the query you provided it's probably equal to ocuk. The point is, you write queries using this constant and they become portable across installations regardless of the users' chosen database prefix.
2) Opencart is based on the MCVL (an extension of MVC which includes language files) structure. The place to execute queries is in the model. The place to call model methods is in the controller. The controller passes output to the view and often uses language variables from language files.
So what I would do is write function and put it in your product model – in this case that's catalog/model/catalog/product.php. This function is called a method since it's now part of your model class. The function will output the rows in your table that a logged in customer has purchased the given product. It's also important to check (a) that the customer is logged in and (b) that the orders you are querying against are real orders - with an order_status_id > 0. We can go into that but suffice to say that you always need to check the order_status_id if you want to know about actual completed orders. Such a method could look something like this:
public function getOrderProductHistory($product_id) {
$result = [];
if ($customer_id = $this->customer->getId()) {
$sql = "
SELECT *
FROM " . DB_PREFIX . "order o
JOIN " . DB_PREFIX . "order_product op USING (order_id)
WHERE o.order_status_id > 0
AND o.customer_id = '" . (int)$customer_id . "'
";
$query = $this->db->query($sql);
$result = $query->rows;
}
return $result;
}
Now in your controller (on a product page that's catalog/controller/product/product.php) you can call this method to get results like this:
$order_product_history = $this->model_catalog_product->getOrderProductHistory($product_id);
And then do something based on the output:
if ($order_product_history) {
$data['has_been_ordered'] = true;
} else {
$data['has_been_ordered'] = false;
}
Now $has_been_ordered is a boolean that you can access in your view to display a message to the customer. There's still a bit more code for you to write but hopefully this helps get you off to a good start.
I have a magento website
We have a store
In a store we have multiple store views (US, EU and UK)
Each store view has it's own currency, etc.
The base currency is GBP at the default config (main)
My problem is that the display currencies work well. Each store view has it's own individual price (no automatic conversion). Everything seems to be working and in order. However, on the final payment emails and actual connection with payment providers (PayPal/Sage). The base currency is always used. Although the display appears in the currency for each store view.
My question is why are the store view currencies not used with PayPal, emails, etc. Although the amounts, display currency, etc work fine?
It turns out that Base Currency can be set on each Store View. However, this option was not presented on the admin side. I had to change the system.xml
app/code/core/Mage/Directory/etc/system.xml
<label>Base Currency</label>
I have to set the appropriate to change from 0 to 1
<show_in_store>1</show_in_store>
Once this was done, I could see Base Currency under "Currency Options" even within a store view. This now works well and everything seems to be working fine.
No PHP code changes or any additional plugins required.
When I had run into this issue with a rather large Magento store, this quick fix worked pretty well for me: Magento knowledge-base paypal base currency tweak
Just note that, that fix probably won't work out of the box but it'll take some tweaking
Here it is some solutions.
You might custom some codes
If you are using Paypal Express,
\app\code\core\Mage\Paypal\Model\Express.php
protected function _placeOrder(Mage_Sales_Model_Order_Payment $payment, $amount)
{
$order = $payment->getOrder();
// prepare api call
$token = $payment->getAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_TOKEN);
$api = $this->_pro->getApi()
->setToken($token)
->setPayerId($payment->
getAdditionalInformation(Mage_Paypal_Model_Express_Checkout::PAYMENT_INFO_TRANSPORT_PAYER_ID))
->setAmount($amount)
->setPaymentAction($this->_pro->getConfig()->paymentAction)
->setNotifyUrl(Mage::getUrl('paypal/ipn/'))
->setInvNum($order->getIncrementId())
**->setCurrencyCode($order->getOrderCurrencyCode())** // should be used getOrderCurrencyCode();
->setPaypalCart(Mage::getModel('paypal/cart', array($order)))
->setIsLineItemsEnabled($this->_pro->getConfig()->lineItemsEnabled)
;
if ($order->getIsVirtual()) {
$api->setAddress($order->getBillingAddress())->setSuppressShipping(true);
} else {
$api->setAddress($order->getShippingAddress());
$api->setBillingAddress($order->getBillingAddress());
}
// call api and get details from it
$api->callDoExpressCheckoutPayment();
$this->_importToPayment($api, $payment);
return $this;
}
\app\code\core\Mage\Paypal\Model\Standard.php
public function getStandardCheckoutFormFields()
{
$orderIncrementId = $this->getCheckout()->getLastRealOrderId();
$order = Mage::getModel('sales/order')->loadByIncrementId($orderIncrementId);
/* #var $api Mage_Paypal_Model_Api_Standard */
$api = Mage::getModel('paypal/api_standard')->setConfigObject($this->getConfig());
$api->setOrderId($orderIncrementId)
**->setCurrencyCode($order->getOrderCurrencyCode())** // should be used getOrderCurrencyCode();
//->setPaymentAction()
->setOrder($order)
->setNotifyUrl(Mage::getUrl('paypal/ipn/'))
->setReturnUrl(Mage::getUrl('paypal/standard/success'))
->setCancelUrl(Mage::getUrl('paypal/standard/cancel'));
// export address
$isOrderVirtual = $order->getIsVirtual();
$address = $isOrderVirtual ? $order->getBillingAddress() : $order->getShippingAddress();
if ($isOrderVirtual) {
$api->setNoShipping(true);
} elseif ($address->validate()) {
$api->setAddress($address);
}
// add cart totals and line items
$api->setPaypalCart(Mage::getModel('paypal/cart', array($order)))
->setIsLineItemsEnabled($this->_config->lineItemsEnabled)
;
$api->setCartSummary($this->_getAggregatedCartSummary());
$api->setLocale($api->getLocaleCode());
$result = $api->getStandardCheckoutRequest();
return $result;
}
I am trying to set up a website that allows about 6 different currencies. I originally was able to achieve this quite well using the currency exchange rates built into Magento and the customer could select which currency they prefered and all prices were displayed in that currency. However I recently discovered that despite the prices being in that currency Magento will still process the transactions in the base currency, in this case GBP. In fact it doesn't tell the customer this is what will happen until right at the end of the order which in my opinion is a very bad thing, some customers might also be charged more by their banks for the currency conversion.
As PayPal has been configured to allow payments in the 6 currencies I would like the customers to be able to pay in those currencies.
I have since found out that due to the way Magento has been built that this is no easy task and instead you should set up a new 'website' for each of the currencies, then I can set a base currency for each website. This sounds easy enough, however the tutorials about this all seem to force each website to have its own unique url - such as either us.website.com or website.com/us - but in this case that will not do and we need everything to use only the one base url website.com.
Does anyone know if this is possible?
I have been able to change the store by manually adding the following to index.php and I was thinking one possible solution would be to remember the users selection in the session and then load the correct store here. Would this be efficient or is there a better way to do this that I have overlooked?
/* Override store code */
if (isset($_GET['code']))
{
$mageRunCode = $_GET['code'];
$mageRunType = 'website';
}
Mage::run($mageRunCode, $mageRunType);
The website is running Magento Community Edition v1.7.0.2.
I have been able to achieve this using something similar to the following in index.php
if (isset($_GET["currency"]) || !isset($_COOKIE["basewebsite"]))
{
// List of available stores
$stores = array('usd','aud','eur');
$website = 'base'; // Default store ID
if (isset($_GET['currency']) && in_array($_GET['currency'], $stores))
{
$website = $_GET['currency'];
}
// Set cookie to remember selection
setcookie("basewebsite", $website);
$_COOKIE['basewebsite'] = $website;
}
else
{
$website = $_COOKIE['basewebsite'];
}
/* Store or website code */
$mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : $website;
/* Run store or run website */
$mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'website';
Mage::run($mageRunCode, $mageRunType);
With this I can then change the website by simply adding ?currency=usd to the end of any Magento url. The selection is then remembered by the cookie and the correct website is loaded with any further requests.
This question hasn't received many views but if anyone does spot a better solution then please do let me know!
I am using Magento 1.7.0.2. Whilst on the product page, if a customer attempts to add a quantity greater than we have in stock they receive a message stating ".. the requested quantity is not available".
Is there any way for magento to either email or log when this occurs? I.e. I receive an automatic email stating a customer has attempted to add X number of item X? This would allow me to identify lost sales due to us not having enough stock of a particular item?
Has anyone come across anything like this before or is this even possible?
Thank you in advance
Mike Prentice
yes this is possible
You have to code for this.
I came across this problem one time and what i have do like this below.
I have make one observer event to check if customer is requesting quantity more then available if so i sent email to admin.
What you can do is create one observer for chekout_cart_add_before event in this event you can put your logic.
Or otherwise you can use magento feature Backorders you can find this in inventory tab,if you enable this then customer can order even requested quantity > available quantity, customer can see one message in cart page about backorder.
There is no standart functionality to notify about low quantity products by email.
But there is RSS notification http://www.magentocommerce.com/wiki/modules_reference/english/mage_adminhtml/system_config/edit/cataloginventory
Extend this functionality to match your needs.
You could write some script which would parse RSS, and send email etc.
EDIT
Here is some extension you may like http://www.magentocommerce.com/magento-connect/low-stock-email-notification.html
But is is not free.
Here's how I've done it so that it sends a google analytics tracking event whenever a customer tries to order more than the available stock level.
First copy: app/code/core/Mage/CatalogInventory/Model/Stock/Item.php
To: app/code/local/Mage/CatalogInventory/Model/Stock/Item.php
so that you're not modifying a core file.
In app/code/local/Mage/CatalogInventory/Model/Stock/Item.php add this function
public function notifyOutOfStock($productId){
$session = Mage::getSingleton('checkout/session');
//Initialise as empty array, or use existing session data
$outOfStockItems = array();
if ($session->getOutOfStock()){
$outOfStockItems = $session->getOutOfStock();
}
try {
$product = Mage::getModel('catalog/product')->load($productId);
$sku = $product->getSKu();
if($sku){
//Add the current sku to our out of stock items (if not already there)
if(! isset($outOfStockItems[$sku]) ) {
$outOfStockItems[$sku] = 0;
}
}
} catch (Exception $e){
//Log your error
}
Mage::getSingleton('checkout/session')->setOutOfStock($outOfStockItems);
}
In that same file is another function called checkQuoteItemQty.
Inside that function you need to call your new function using $this->notifyOutOfStock($this->getProductId()); right after it sets each of the error messages and before the return statement.
So:
public function checkQuoteItemQty($qty, $summaryQty, $origQty = 0)
{
....
if ($this->getMinSaleQty() && ($qty) < $this->getMinSaleQty()) {
$result->setHasError(true)
->setMessage(
$_helper->__('The minimum quantity allowed for purchase is %s.', $this->getMinSaleQty() * 1)
)
->setQuoteMessage($_helper->__('Some of the products cannot be ordered in requested quantity.'))
->setQuoteMessageIndex('qty');
//** Call to new function **
$this->notifyOutOfStock($this->getProductId());
return $result;
}
.....
->setQuoteMessageIndex('qty');
//** Call to new function **
$this->notifyOutOfStock($this->getProductId());
return $result;
.....
What this does is add your product sku to an array in the checkout session.
This means you will have access to that info in the template file right after your page loads displaying the "Insufficient stock" notification.
So in one of your template files you can add some code to render the necessary JavaScript.
I've chosen header.phtml since it loads on every page. (Users can add quantities of items to the cart in the cart page as well as the product view page).
app/design/frontend/CUSTOMNAME/default/template/page/html/header.phtml
Somewhere down the bottom of the code add this:
<!-- GA tracking for out of stock items -->
<script>
try {
<?php
$session = Mage::getSingleton('checkout/session');
if ($session->getOutOfStock()){
$outOfStockItems = $session->getOutOfStock();
foreach($outOfStockItems as $sku=>$value) {
if($value==0){
//Render the GA tracking code
echo "_gaq.push(['_trackEvent', 'AddToCart', 'ProductQtyNotAvailable', '".$sku."']); \r\n";
//Set it to 1 so we know not to track it again this session
$outOfStockItems[$sku] = 1;
}
}
//Update the main session
Mage::getSingleton('checkout/session')->setOutOfStock($outOfStockItems);
}
?>
}
catch(err) {
//console.log(err.message);
}
</script>
Can confirm this works well and in my opinion is better than an email or RSS feed as you can analyse it along with the rest of your analytics.