I have a custom object "Order" with detail object "Order Lines". I want to place a custom button on the Quote header called "Create Order" that when clicked will copy the Quote header and detail records and create a corresponding "Order" and "Order Lines" records.
Mimicing the same functionality that is standard when syncing a quote to an opportunity.
I'm wondering what is the best approach to do this? Should the custom button use the ajax toolkit and execute javascript using the onclick event?
Or, would a visualforce page with a controller be a better option for this?
Are there any examples that demonstrate this functionality?
Thank you.
I'd go with a bit of Apex code. Faster than doing it purely with JavaScript toolkit plus let's face it, S-Controls are thing of the past. There's a chance that if your colleague will have to maintain it in future he'll know Apex but maybe not the JS toolkit.
Now - simple JS button + call to Apex exposed with webservice keyword or a visualforce page?. Only you can answer that. Would you like to display the "edit" page, let user change some values and save only afterwards? Are there some statuses on order or line item that have to be "reset" before you save the clone?
As for actual code - best type of clone will dynamically select all (so it'll work also in future when you add new fields). Sadly there's no SELECT * syntax in Apex, you need to use describe calls.
Something like this (untested! just to give you an idea)
List<Schema.SObjectField> orderFields = Schema.SObjectType.Order__c.fields.getMap().values();
List<Schema.SObjectField> itemFields = Schema.SObjectType.Order_Line__c.fields.getMap().values();
// You might also want to use simple fields.getMap().keyset(), that'll be set of strings
Id orderId = 'a01...';
String orderQuery = 'SELECT ' + String.join(orderFields, ', ') + ' FROM Order__c WHERE Id = :orderId';
Order__c oldOrder = Database.query(orderQuery);
Order__c newOrder = oldOrder.clone(false,true); //options do not retain ID; deep clone
insert newOrder;
String itemsQuery = 'SELECT ' + String.join(itemFields, ', ') + ' FROM Order_Line__c WHERE Order__c = :orderId';
List<Order_Line__c> oldLines = Database.query(itemsQuery);
List<Order_Line__c> newLines = oldLines.deepClone(false);
for(Order_Line__c newLine : newLines){
newLine.Order__c = newOrder.Id;
}
insert newLines;
Feel free to experiment with it (refactor to a helper method, work on generic sObjects instead of concrete Orders... after all if lookup change is the only thing to do - why not?). Also if you're sure orders will have < 200 lines you can try making the query in one go?
Related
I'm using FeathersJS and ReactJs to build an application and now I am stuck in a certain point.
The user interface I'm working on is a table and I have a navigation bar to control the data exhibited, like in the image.
In the select users may choose how many results they are going to see in a page. The navigation buttons move among different pages. Every time one of these thing change I calculate
let skip = (this.state.page - 1) * this.state.limit;
let url = '/users?$sort[name]=1&$skip=' + skip + '&$limit=' + this.state.limit;
and then make a REST call to Feathers backend. This is working perfectly well.
The red arrow indicates my problem.
When the user types part of a name in this input field I need to search the Users table to find all users with the typed string in the name, whatever the position and preferably case insensitive. In this case I am creating an url like
let url = '/users?name=<typed input'>;
and then making the REST call.
It happens that this call will try to find a name equal to , and this is not what I need. Then I am assuming I'll have to customize my find() method to perform differently when it receives the two different sets of parameters, like in
users.class.js
const { Service } = require('feathers-sequelize');
exports.Users = class Users extends Service {
find(data,params) {
if (data.query.name) {
// Do something to get the search I need
}
return super.find(data,params);
}
};
But I really don't know to proceed from this point on.
Should I implement a Sequelize raw query inside this if? What would be the best way to get the data I need?
This isn't really a Feathers question, or even a Sequelize question, but a database function. It depends on what your database is! For MySQL, you can use the LIKE command to search for a substring. Google how to make it case insensitive, if need. For the Sequelize query I believe something like this would work:
query = {
name: { $like: '%searchterm%'}
}
I want to create a salesforce app. That have trigger on user creation. I need to make it optional by including a checkbox on user creation page.(i.e : the trigger have to start only when my custom checkbox is selected).
How to add custom checkbox in "User creation" page programatically using Apex ?Is it possible or not ?
I tried with MetaData API as :
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
List<MetadataService.Metadata> fields = new List<MetadataService.Metadata>();
MetadataService.CustomField customField = new MetadataService.CustomField();
customField.fullName = 'User.custom_create_field__c';
customField.label = 'Custom created field';
customField.defaultvalue = 'false';
customField.sharingModel = 'ReadWrite';
customField.type_x = 'Checkbox';
fields.add(customField);
MetadataService.AsyncResult[] results = service.create(fields);\\trows session timeout error here
But the last line throws session timeout error. Am I going in correct way to achieve my need? If so how to solve session timeout error?
Why do you want to do this programmatically? What's the problem with adding the checkbox declaratively to the object and to the layouts?
By adding the field to the object metadata as you're doing here, you'll still need to manually add it to layouts to ensure it's available for users to check or uncheck on your user creation page. Adding a new field to all custom layouts is not a trivial exercise, believe me!
However, if you still want to go ahead with this, you should try a different approach. It looks very much to me as though you've taken inspiration from Andy Fawcett here:
https://andyinthecloud.com/2013/10/27/introduction-to-calling-the-metadata-api-from-apex/
You should see the IMPORTANT UPDATE section. The async Metadata API methods were deprecated some time ago, this is the pattern to follow now:
https://andyinthecloud.com/2014/08/14/apex-metadata-api-streamlined-and-simplified-for-summer14/
I am having 2 pages
parent page consist a link, on click of this link i am calling an javascript function
function callPage( productId ){
var product = document.getElementById(ProductId).id;
var OpptyId = {!oppty.Id};
var urlToOpen = "/apex/" + '{!namespacePrefix}' + 'testPage?product='+product+'&OpptyId'=OpptyId ;
window.open(urlToOpen ,'','resizable=0,location=0,status=0,scrollbars=0,width=850,height=350,top=100,left=220')
}
where Oppty Id is taken from controller.
While in my child page I am getting all these paramiters as
ProductValueId = ApexPages.currentPage().getParameters().get('product');
opptyValue = ApexPages.currentPage().getParameters().get('OpptyId');
and these fields are then used in javascript as
function setValue()
{
var productId = '{!productValueId}';
window.parent.opener.document.getElementById(productId).value='{!productValue}';
}
where {!productValue} is taken from controller.
I am not understanding how and where should i make changes for soving my checkmarx issue.
So, please help me as I would want to submit the application for code review.
Best Regards
You are taking the query string parameter product, reading it into the productValueId controller member/property and then directly outputting it into the Visualforce page.
So basically whatever I give you on the query string ends up output into the page response.
With some effort it may be possible to encode a query string that will break out of your JavaScript and execute whatever I wan't.
E.g.
/apex/ABC__testPage?product=productId';alert('xss&OpptyId=006100000000001
Or something like that. To be fair, Visualforce will encode the expression for you.
Checkmarx has found the potential path. You will need to either remove this direct path or provide sufficient justification that it isn't open to XSS.
One helpful thing to do is enforce the data type on the values read from the query string. E.g. Explicitly use Id rather than string as the type for productValueId.
Then you could also verify that the records referenced in the query string params are actually valid for the current user. I.e. they haven't changed some of the values to gain access to records they otherwise shouldn't see.
Incidentally, the Salesforce StackExchange is a great place to ask Salesforce specific questions.
Breeze & Angular & MV*
I get an invoice object and expand it's necessary properties: Customer, Details, etc.
To access detail properties is easy, invoice.detail[n].property. And saving changes to existing properties (1 - n) is also easy. In my UI, I simply loop through my object vm.invoice.details to get & display all existing details, bind them to inputs, edit at will, call saveChanges(), done!
(keep in mind, in this UI, I need to complete the following too....)
Now, I have blank inputs for a new detail I need to insert.
However, I need to insert a new detail into the existing array of invoice details.
For example: invoice #5 has 3 details (detail[0], detail[1], detail[2]). I need to insert into this existing invoice, detail[3], and call saveChanges()
I've tried to call the manger.createEntity('invoice') but it complains about FK constraints. I know you can pass values as a second argument in createEntity('obj', newvalues)...but is that the correct and only method?
Seems like this should all be much easier but, well, I am at a loss so, please help where you can. TIA!
Take a look at the DocCode sample which has tests for all kinds of scenarios including this one.
Perhaps the following provides the insight you're looking for:
function addNewDetail() {
var newDetail = manager.createEntity('Detail', {
invoice: vm.currentInvoice,
... other initial values
});
// the newDetail will show up automatically if the view is bound to vm.details
}
Notice that I'm initializing the parent invoice navigation property. Alternatively, I could just set the Detail entity's FK property inside the initializer:
...
invoiceId: vm.currentInvoice.id,
...
Either way, Breeze will add the new detail to the details collection of the currentInvoice.
Your question spoke in terms of inserting the new Detail. There is no need to insert the new Detail manually and you can't manage the sort order of the vm.currentInvoice.details property any way.
Breeze has no notion of sort order for collection navigation properties.
If you need to display the details in a particular order you could add a sorting filter to your angular binding to vm.currentInvoice.details.
Make sure you have correct EntityName, because sometimes creating entity is a not as simple as it seems.Before working with entities see
http://www.getbreezenow.com/documentation/creating-entities
I will suggest you to look ur metadata file, go to last line of your file, you can see the field named "entitySet"
"entitySet":{"name":"Entity_Name","entityType":"Self.Entity_Name"}
check the entityName here i took as "Entity_Name" and then try to create the entity and use this name
manger.createEntity('Entity_Name');
I am trying to display case with CaseFeeds and FeedComments on my console app support page. Now when users reply to a case I want to insert a CaseFeed from my application. As per the salesforce API docs, create or upsert is not supported on CaseFeed.
http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_casefeed.htm
Is there any way or work around to create a CaseFeed under Case?
CaseFeed it like a view - a combination of all kinds of activities happening to the Case.
It's bit similar to how Events and Tasks can be viewed together on a related list of Open Activities. Check the Type field to see what does it aggregate. It kind of makes sense for a view to be readonly.
You can imitate user posting to that Case instead - that'd be FeedItem object.
insert new FeedItem(
ParentId = '50070000006Rn5M',
Title='Some title',
Body = 'Some long text goes here',
Type='TextPost'
);