Opencart: how to change item price when adding it into the cart - opencart2.x

I am trying to implement a functionality in opencart where it will be possible to enter a custom price at the product page via text area and when item is added to the cart if custom price entered it will apply the price specified in the custom price field.
Similar question was asked here someone kindly provided a good solution which applies to OpenCart 1.5.x. However I have tried to follow this approach on OpenCart 2 without any success. I have checked everything over and over for the last few days but I don't seem to be able to get this working as I am a newbie in programming world
I wondering if anyone is able to point me to right direction to what I may be missing.
I have search the web but unable to find any relevant information
I have checked and noticed that AJAX request is changed to #product div in 2.x, so I have enter my price input within this div underneath the quantity
<input name="custom_price" id="custom_price" value="" title="custom_price" class="input-text custom_price" type="textarea">
I have than moved on to the controller checkout/cart/add within the Add() method I have added this code
if(isset($this->request->post['custom_price'])) {
$custom_price = $this->request->post['custom_price'];
} else {
$custom_price = false;
}
Further down, I have changed this line
$this->cart->add($this->request->post['product_id'], $this->request->post['quantity'], $option, $recurring_id);
to:
$this->cart->add($this->request->post['product_id'], $this->request->post['quantity'], $option, $custom_price, $recurring_id);
Next, in the system/library/cart.php I have change the definition of the Add() method to the following
public function add($product_id, $qty = 1, $option = array(), $recurring_id = 0, $custom_price = false) {
Before the end of the Add()method I have added the following
if($custom_price) {
if(!isset($this->session->data['cart']['custom_price'])) {
$this->session->data['cart']['custom_price'] = array();
}
$this->session->data['cart']['custom_price'][$key] = $custom_price;
}
Within the GetProduct() I have added these lines
if(isset($this->session->data['cart']['custom_price'][$key])) {
$price = $this->session->data['cart']['custom_price'][$key];
}
right after this line:
$price = $product_query->row['price'];
Finally at after the array where product price is set to price + option price
'price' => ($price + $option_price),
I have added the following
if(isset($this->session->data['custom_price'][$key])) {
$this->data[$key]['price'] = $this->session->data['custom_price'][$key];
}

Related

pdfkit nodejs, one element per page from page 2

Im using pdfkit to generate pdf invoice.
When all my content fit in one page I have no issue.
However when it doesn't fit and need an extra page, I have a strange behaviour:
Instead of adding the elements in the second page, it only add one line and the rest of the page is blank.
Then on 3rd page I have another element, and the rest it blank, then 4th page, 5th etc.
Here is the code corresponding to this part:
for (let i = 0; i < data.items.length; i++) {
const item = data.items[i];
this.itemPositionY = this.itemPositionY + 20;
if (item.bio) this.containBioProduct = true;
let itemName = item.bio ? `${item.item}*` : item.item;
this.generateTableRow(
doc,
this.itemPositionY,
itemName,
"",
this.formatCurrency(item.itemPriceDf.toFixed(2)),
item.quantity,
this.formatCurrency(item.itemPriceTotalDf.toFixed(2))
);
this.generateHr(doc, this.itemPositionY + 15);
}
Basically I just iterate over an array of products. For each line my Y position has +20.
Thanks for your help.
In case someone has this issue, here is a solution:
Everywhere in the code I know that an extra page could be generated, I add this:
if (this.position > 680) {
doc.addPage();
this.position = 50;
}
It allows you to control the generation of new pages (instead of pdfkit doing it automatically with potential problems)
You just need to track the position from the initialization of "this.position".
In that way, evertime it's superior than an Y position (680 in my case, it's a bit less than a page with pdfkit), you just do "doc.addPage()", which will create another page, and you reinitialize your position to the beginning of the new page.

Hyperlink to page with smart search filter prepopulated

I have a smart search page that shows all the products on the page with some smart search filters that narrows down the products on some criterias (Let's say for example Filter1 has Option1, Option2 and Option3).
What I am trying to accomplish is to have a link on a seperate page that links to the product page, but when the user clicks on that link some of the search filters gets set (For example Filter1 would have Option2 selected).
I'm not sure if that is possible with out of the box solution, but with simple tweaks inside SearchFilter.ascx.cs, you can make a workaround. File is placed under CMSWebParts/SmartSearch/SearchFilter.ascx.cs. You should change method 'GetSelectedItems' to take a look into query string for filter value (see snippet bellow):
/// <summary>
/// Gets selected items.
/// </summary>
/// <param name="control">Control</param>
/// <param name="ids">Id's of selected values separated by semicolon</param>
private string GetSelectedItems(ListControl control, out string ids)
{
ids = "";
string selected = "";
//CUSTOM: retrive value for query string
var customFilter = QueryHelper.GetString("customFilter", "");
// loop through all items
for (int i = 0; i != control.Items.Count; i++)
{
//CUSTOM: ----START-----
if (!RequestHelper.IsPostBack())
{
if (!string.IsNullOrEmpty(customFilter))
{
if (control.Items[i].Text.Equals(customFilter, StringComparison.InvariantCultureIgnoreCase))
{
control.Items[i].Selected = true;
}
}
}
//CUSTOM: ----END-----
if (control.Items[i].Selected)
{
selected = SearchSyntaxHelper.AddSearchCondition(selected, control.Items[i].Value);
ids += ValidationHelper.GetString(i, "") + ";";
}
}
if (String.IsNullOrEmpty(selected) && (control.SelectedItem != null))
{
selected = control.SelectedItem.Value;
ids = control.SelectedIndex.ToString();
}
return selected;
}
And your hyperlink will look like this: /Search-result?searchtext=test&searchmode=anyword&customfilter=coffee
With this modifications, you can send only one value in filter, but if you need more then one value, you can send them and customize it however suits you best. Also, you can send filter name (in case that you have multiple filters) and then add check in method above.
I will recommend you not to modify kentico files. Instead of that, clone default filter web part and make modifications there, because withing next upgrade of project, you will lose your changes. I checked this in Kentico 11.
For Smart Search Filters:
if turn off auto-post back option -then web part control ID should become a query string parameter that you can use.
This above will form something like:
/Smart-search-filter.aspx?searchtext=abc&searchmode=anyword&wf=2;&ws=0;&wa=0
P.S. I suggest you to take a look at the corporate site example: look the smart search filter web part: /Examples/Web-parts/Full-text-search/Smart-search/Smart-search-filter. It is working example you can use it as starting point.

Calculated Field on CRM Entity

I have a 1:N relationship between Account and Portfolios in Dynamics CRM
I.e each account has multiple Portfolios and Each Portfolio has Specific Assets.
I am trying to create a field on Account Form which calculates the sum of "ALL Assets of All related portfolios" of the account and display it on the Account form
As a workaround,I tried to create a Portfolio view grouping by Account but it doesnt SUM and rollup the Portfolio assets to Account level.
So on account Form i am trying to create a textfield which calculates the Total Account Assets to be $25,000 in this example
function setupGridRefresh() {
var targetgrid = document.getElementById("NAME OF SUBGRID");
// If already loaded
if (targetgrid.readyState == 'complete') {
targetgrid.attachEvent("onrefresh", subGridOnload);
}
else {
targetgrid.onreadystatechange = function applyRefreshEvent() {
var targetgrid = document.getElementById("NAME OF SUBGRID");
if (targetgrid.readyState == 'complete') {
targetgrid.attachEvent("onrefresh", subGridOnload);
}
}
}
subGridOnload();
}
function subGridOnload() {
//debugger;
var grid = Xrm.Page.ui.controls.get('NAME OF SUBGRID')._control;
var sum = 0.00;
if (grid.get_innerControl() == null) {
setTimeout(subGridOnload, 1000);
return;
}
else if (grid.get_innerControl()._element.innerText.search("Loading") != -1) {
setTimeout(subGridOnload, 1000);
return;
}
var ids = grid.get_innerControl().get_allRecordIds();
var cellValue;
for (i = 0; i < ids.length; i++) {
if (grid.get_innerControl().getCellValue('FIELD NAME LOWER CASE', ids[i]) != "") {
cellValue = grid.get_innerControl().getCellValue('FIELD NAME LOWER CASE', ids[i]);
cellValue = cellValue.substring(2);
cellValue = parseFloat(cellValue);
sum = sum + cellValue;
}
}
var currentSum = Xrm.Page.getAttribute('DESTINATION FIELD').getValue();
if (sum > 0 || (currentSum != sum && currentSum != null)) {
Xrm.Page.getAttribute('DESTINATION FIELD').setValue(sum);
}
}
I pieced this together from a couple of sources and currently use it one of my solutions. Let me know if you need some more help or if I've misread the question. (Btw, this solution is based on the assumption that you need the total to change when the subgrid has entries added or removed. If this is not the requirement, I would suggest the RetrieveMultiple OData call.)
Take a look at AutoSummary from Gap Consulting, well worth the cost. Or spend time to build your own. You need a field on the Account record which is updated every time you:
create a Portfolio record
update the value in a Portfolio record
delete a Portfolio record
re-parent a Partfolio record from one Account to another
The first two are easy enough to do with workflow or javascript on the onSave event on the portfolio. Third can only be done by workflow, not javascript (I think). Last one would need onLoad javascript to store current value of Account lookup so that onSave can compare and then decrement one and increment the other. All four could be done with a plugin.
Although this has been answered already, I'll put a second option on the plate for you. Take a look at FormulaManager from North 52 as well. You get a certain amount of Formulas for free so it might be an even more cost effective solution.
Update
To add to this, if the field is solely for reporting a value (and doesn't need to be saved to the database) then rather than using a physical field and plugins you could build a Web Resource that executes an Aggregated FetchXml query and simply displays the resulting value.
Again, this is something that I know Formula Manager does out of the box. Have never used Auto Summary.

Enterprise Keyword not updating in SharePoint 2010

Any idea how to inject values to the Enterprise Keywords column of a List / Doc Lib Item using code?
Tried the following, it didn't give any error, but that column wouldn't update, while the Title did.
using (var site = new SPSite("http://testweb"))
{
using (var web = site.OpenWeb("testsite1"))
{
var list = web.Lists["testlist1"];
var item = list.AddItem();
item["Title"] = string.Format("Injected from code on {0}", DateTime.Now.ToString());
item["Enterprise Keywords"] = "1;#Opera|4eed0518-9676-4afc-be20-9027b3b69e42";
item.Update();
}
}
In this code, Opera keyword has been added previously, I've checked it against the TaxonomyHiddenList list as well using code to extract the correct ID and IdForTerm (the GUID).
What am I missing here?
To add a taxonomy field value the approach is a little bit different. Please try:
TaxonomyField entKeyword = (TaxonomyField)item.Fields["Enterprise Keywords"];
TaxonomyFieldValue term = new TaxonomyFieldValue("1;#Opera|4eed0518-9676-4afc-be20-9027b3b69e42");
entKeyword.SetFieldValue(item,term);
in stead of:
item["Enterprise Keywords"] = "1;#Opera|4eed0518-9676-4afc-be20-9027b3b69e42";

How can I set the default value in a SharePoint list field, based on the value in another field?

I have a custom list in SharePoint (specifically, MOSS 2007.) One field is a yes/no checkbox titled "Any defects?" Another field is "Closed by" and names the person who has closed the ticket.
If there are no defects then I want the ticket to be auto-closed. If there are, then the "Closed by" field ought to be filled in later on.
I figured I could set a calculated default value for "Closed by" like this:
=IF([Any defects?],"",[Me])
but SharePoint complains I have referenced a field. I suppose this makes sense; the default values fire when the new list item is first opened for entry and there are no values in any fields yet.
I understand it is possible to make a calculated field based on a column value but in that case the field cannot be edited later.
Does anyone have any advice how to achieve what I am trying to do?
Is it possible to have a "OnSubmit" type event that allows me to execute some code at the point the list item is saved?
Thank you.
Include a content editor web part in the page (newform.aspx / editform.aspx) and use jQuery (or just plain javascript) to handle the setting of default values.
Edit: some example code:
In the lists newform.aspx, include a reference to jquery. If you look at the html code, you can see that each input tag gets an id based on the field's GUID, and a title that's set to the fields display name.
now, using jquery we can get at these fields using the jQuery selector like this:
By title:
$("input[title='DISPLAYNAMEOFFIELD']");
by id (if you know the field's internal guid, the dashes will ahve to be replaced by underscores:
// example field id, notice the guid and the underscores in the guid ctl00_m_g_054db6a0_0028_412d_bdc1_f2522ac3922e_ctl00_ctl04_ctl15_ctl00_ctl00_ctl04_ctl00_ctl00_TextField
$("input[id*='GUID']"); //this will get all input elements of which the id contains the specified GUID, i.e. 1 element
We wrap this in the ready() function of jQuery, so all calls will only be made when the document has fully loaded:
$(document).ready(function(){
// enter code here, will be executed immediately after page has been loaded
});
By combining these 2 we could now set your dropdown's onchange event to the following
$(document).ready(function(){
$("input[title='DISPLAYNAMEOFFIELD']").change(function()
{
//do something to other field here
});
});
The Use jQuery to Set A Text Field’s Value on a SharePoint Form article on EndUserSharePoint.com shows you how to set a default value for a field using JavaScript/jQuery.
They also have a whole series of articles on 'taming calculated columns' that will show you many more powerful options you have for calculated fields with the use of jQuery.
One thing to be aware of when inserting JavaScript into a SharePoint page and modifying the DOM is support. There is a small chance that a future service pack will break the functionality you add, and it is quite likely that the next version of SharePoint will break it. Keeping this mind however, I believe it's a good solution at this time.
I've got a walk through with sample code that may help
Setting a default duration for new calendar events
It sets the End Time/Date fields to Start Time + 1.5 hours when you create a new event.
Its complicated a little by the steps need to do the time/date work, but you'll see examples of how to find the elements on the form and also one way to get your script onto the newform.aspx without using SPD.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>
<script type="text/javascript">
// Set the hours to add - can be over 24
var hoursToAdd = 1;
// Mins must be 0 or div by 5, e.g. 0, 5, 10, 15 ...
var minutesToAdd = 30;
// JavaScript assumes dates in US format (MM/DD/YYYY)
// Set to true to use dates in format DD/MM/YYYY
var bUseDDMMYYYYformat = false;
$(function() {
// Find the start and end time/minutes dropdowns by first finding the
// labels then using the for attribute to find the id's
// NOTE - You will have to change this if your form uses non-standard
// labels and/or non-english language packs
var cboStartHours = $("#" + $("label:contains('Start Time Hours')").attr("for"));
var cboEndHours = $("#" + $("label:contains('End Time Hours')").attr("for"));
var cboEndMinutes = $("#" + $("label:contains('End Time Minutes')").attr("for"));
// Set Hour
var endHour = cboStartHours.attr("selectedIndex") + hoursToAdd;
cboEndHours.attr("selectedIndex",endHour % 24);
// If we have gone over the end of a day then change date
if ((endHour / 24)>=1)
{
var txtEndDate = $("input[title='End Time']");
var dtEndDate = dtParseDate(txtEndDate.val());
if (!isNaN(dtEndDate))
{
dtEndDate.setDate( dtEndDate.getDate() + (endHour / 24));
txtEndDate.val(formatDate(dtEndDate));
}
}
// Setting minutes is easy!
cboEndMinutes.val(minutesToAdd);
});
// Some utility functions for parsing and formatting - could use a library
// such as www.datejs.com instead of this
function dtParseDate(sDate)
{
if (bUseDDMMYYYYformat)
{
var A = sDate.split(/[\\\/]/);
A = [A[1],A[0],A[2]];
return new Date(A.join('/'));
}
else
return new Date(sDate);
}
function formatDate(dtDate)
{
if (bUseDDMMYYYYformat)
return dtDate.getDate() + "/" + dtDate.getMonth()+1 + "/" + dtDate.getFullYear();
else
return dtDate.getMonth()+1 + "/" + dtDate.getDate() + "/" + dtDate.getFullYear();
}
</script>

Resources