Is there a way to query all published promotions in hybris along with their qualifying products? After add-to-cart we can always get them using the field allPromotionResults. Suppose I want to fetch all the promotions before add to cart, which classes do I need to join.
Query I tried:
select * from {AbstractPromotion as ap} where
({ap.startDate} >= '2019-02-01T00:00:00.000'
AND {ap.endDate} <='2019-07-30T00:00:00.000' AND {ap.enabled}=1)
How do I filter the unpublished ones and how do I get the qualifying products?
From what I know allPromotionResults is returning the promotions that were applied on the cart which means that the
conditions of the Promotions were fulfilled for that particular cart.
The conditions of the Promotion are stored as a Json (i.e SourceRule#conditions).
Because of that, in order to get all the published promotions that use at lease one "qualifying product" condition you can use:
select * from {PromotionSourceRule as PSR } where {PSR:status} = ({{select distinct(PK) from {RuleStatus} where {code} = 'PUBLISHED' }}) and {PSR:conditions} like '%y_qualifying_products%'
where 8796113010779 is the PK of the Published enum value(Can be found in backoffice-> System -> Types).
Related
I could get record and parent object details using lookup fields. But I am not able to get parent object record type.
Example : I am trying to get contact details and parent(company) details.
search.lookupFields({
type: 'contact',
id: context.recordId,
columns: ['entityid', 'customer.entityid', 'customer.companyname']
});
It returns Contact entityid and customer - entityid & companyname.
I want customer type too. It can be Prospect, Lead, Customer or any valid types,
for partner or vendor I have to specify partner.entityid or vendor.entityid.
Anyway to identify which parent company contact has
something like this customer.recordtype
In NetSuite, the term "Customer" is used for both a record type and a stage within the Customer type - which can be a little confusing.
Lead, Prospect and Customer are all stages, and this can be returned using customer.stage as a value in the columns parameter. When you do this, it will be returned as an array of objects with value and text properties, so you would need to reference it like <varName>['customer.stage'][0].value. This only works if you know the type is customer - otherwise the stage would be empty.
If the problem you're facing is actually to get the record type - IE: customer, vendor, partner etc, then you can instead use company.type.
look up stage
search.lookupFields({
type: 'contact',
id: context.recordId,
columns: ['entityid', 'customer.entityid', 'customer.companyname', 'customer.stage']
});
Have you tried looking for the customer.stage field?
If you load the record, you can use the
recordObj.getRecordType()
apicall in suitescript 1.0, and
record.type
apicall in Suitescript 2.0.
Having said that, I was looking for information on the field so that I wouldn't have to load the whole record. Perhaps available through nlapiLookupField (and its equivalent in 2.0).
var recTypeId = nlapiLookupField('contact', 'xxxxx', 'company.type', false)
This returns 'vendor' or 'customer' depending on what the contact refers to. Presumably after determining this, you can lookup vendor fields by specifying 'vendor' as the type, or the field as 'vendor.field'
In Maximo, I want to retrieve the most recent status memo and add the WOSTATUS.MEMO field to Work Order Tracking Module via Application Designer. In the Work Order Tracking application, to see the same information, you'd go to an individual Work Order > Select Action > View > Work Order History.
You may have noticed the WOSTATUS relationship on the WORKORDER object and found that you can't control which of the many WOSTATUS records for this work order gets chosen for showing the memo. You'll need to make a copy of this relationship which specifically finds the latest record. To find that latest record, you could go for the WOSTATUS record with the CHANGEDATE matching the STATUSDATE on the work order or with the highest WOSTATUSID. Assuming you go for the former, because it doesn't require a subquery, you'll create a new relationship from WORKORDER to WOSTATUS called LASTSTATUS with a where clause like this:
wonum = :wonum and siteid = :siteid
and status = :status and changedate = :statusdate
You can then use the standard Relationship.Attribute syntax for the Attribute property of a Textbox in App Designer: LASTSTATUS.MEMO.
In case you were interested, here's the where clause you would use if you wanted to go for the WOSTATUSID instead:
wonum = :wonum and siteid = :siteid
and wostatusid = (
select max(wostatusid)
from wostatus
where wonum = :wonum and siteid = :siteid
)
(Some may argue the toss about whether the first line in the above query is needed. I would respond with the suggestion to test for performance / optimal execution plan in your database environment.)
I hope that helps.
We have a requirement to delete certain products from everywhere in hybris (including cart, orders, promotions) and all its references as well like Media, Category, Stocks, etc.
I found this one solution:
REMOVE Product [batchmode=true];itemType(code)[unique=true]
;Product;
I was wondering if just deleting the product, would remove all its references from hybris, or is there any better solution to do this.
Any help is greatly appreciated!
Removing the product will remove references to it, but not the objects which are refering to it (like Media, Category, Stocks, etc.)
The only objects which will be deleted are those refered by attributes with the partOf modifier.
A Part Of relationship between two classes extends an aggregation
relationship by ensuring that the lifecycle of the dependant object
(the part) is bound to the lifecycle of the parent object. When you
delete the parent object, all instances of its attribute types that
are marked as partOf will then be cascade-deleted.
Hybris doesn't know if a Media or a Category is no longer needed after a Product is removed. Therefore you must delete those objects explicitly.
Removing product will remove only instances of product type, but not all data like media.
To remove from cart : it should inform user that product no longer available in store
For successfully placed orders : you should be able to display basic details of product with message as in cart [ :) :) But you should deliver if order is placed successfully and payment is received otherwise its a bad eCommerce impression]
For promotions : you should remove all promotions related to this product Or reconfigure according to business need.
Is this possible to set or override the internalid of a custom record type ?
The internalid is self generated, but I want to try to set inernalid value from a cvs field.
I do not believe you can override the internalid of any record instance, even a custom one. You can, however, use the externalid field that I believe every record has if you want to specify a "secondary" identifier for the record.
You cannot override the internal ID of a record. As mentioned in a previous response to your question, you can (and should) set the external ID field on the NetSuite records to equal the primary key of the data being imported.
On future imports that update NetSuite data, you simply map the key field of the imported data to the external ID field in NetSuite. You can ignore the internal ID field on those future imports, as Netsuite will match up the records based upon the external ID.
This is the proper way to do this - I've seen tons of situations where people did not understand the external ID concept, and created huge, long term maintenance issues by not following this simple solution.
OK. Given your feedback, why not create another custom field on the customer record that references the lookup value in the custom record?
The field type should be the same as the field type of the lookup value, with "store value" checkbox unchecked. On the Sourcing and Filtering tab, you specify the custom record type and field to reference. Think of it as a SQL join of sorts between the customer record and custom record.
Then, you should be able to do what you want with 2 getCurrentAttribute tags:
<%= getcurrentattribute('cusomter', 'custentity_mappingid')%>
<%= getcurrentattribute('cusomter', 'custentity_mappingvalue')%>
BTW, your custom field internal IDs look a little odd. They should start with 'custentity', and 'custrecord', respectively. My code above reflects what you'd normally expect from NetSuite.
You can set ExternalId and if you want to get record then you can use callGetRecordByExternalId .
public ReadResponse callGetRecordByExternalId(String externalId, RecordType recordType) throws RemoteException {
return this.callGetRecord(Utils.createRecordRefWithExternalId(externalId, recordType));
}
Background (ie what the heck is a relative complement?)
Relative Complement
What I'm trying to do
Let's say I've got a custom Vehicle entity that has a VehicleType option set that is either "Car", or "Truck". There is a 1 to many relationship between Contact and Vehicle (ie. ContactId is on the vehicle entity). How do I write an XRM query (Linq To CRM, QueryExpression, fetch Xml, whatever) that returns the contacts with only cars?
Option 1:
I’d prefer a modification of the proposal that AdamV makes above. I can’t think of a way that you’d get this particular query answered using Linq to CRM, Query Expressions, FetchXML alone. Daryl doesn’t offer what the client is, but I would suppose if Linq and Query Expressions were acceptable offerings, .NET is on the table. Creating aggregate fields containing the count of the related entity on the parent entity (contact in this case) offers more than the Boolean option. If the query requirements ever changed to a threshold (more than X cars, less than Y trucks, between X and Y total vehicles) the Boolean options fails to deliver. The client in this question isn’t known, but I can’t think of many (any?) cases where pulling all the records to the client on a set of 500K+ rows is more efficient than a the SQL query that CRM would make on your behalf against several integer fields with range clauses.
Upside:
Maintains client purity in Query approach
Simple client query
Probably as performant as possible
Downside:
Setups for Aggregate fields
Workflow or plugin to manage the increment and decrement of the aggregate fields
SQL Script for initial load of the aggregates.
Risk that aggregate fields get out of sync (workflow or plugin fails)
Option 2:
If purity within the client isn’t essential, and .NET is on the table – skip the aggregate fields and the setup and just run SQL against the Views. If you don’t want to work with the ADO.NET, a thin ORM like Dapper, Massive, or PetaPOCO can still give you an object model. As Andreas offers in his comment on the OP’s first answer, it seems like something fairly trivial to do in SQL.
Sketching something from top of mind:
SELECT c.*
FROM Contact
WHERE C.Contactid in (
Select contactid
FROM Vehicle v
group by v.contactid , v.type
having v.type = ‘Car’ and count(contactid) > 1
)
AND NOT IN (
Select contactid
FROM Vehicle v
group by v.contactid , v.type
having v.type <> ‘Car’ and count(contactid) > 1
)
Upside:
Much less work
CRM Entities get left alone
Downside:
Depending on the client and/or the application mixing DataAccess methods is a bit kludgy.
Likely less performant than Option 1
Option 3:
Mix and Match: Take the aggregate fields from Option 1. But update them using a scheduled SQL job (or something similar) with a query similar to the initial load job you’d need to write in Option 1
Upside:
Takes most of the work and risk out of Option 1
Keeps all of the performance of Option 1
Downside:
Some will see this as an unsupported feature.
In order to order to perform a true Relative Complement Query you need to be able to perform a subquery.
Your query would basically say give me all the contacts with cars, and then, within those results, remove any contacts that have a vehicle that isn't a car. This is what the SQL in #JasonKoopmans answer does. Unfortunetly, CRM does not support SubQueries.
Therefore, the only way to achieve this is to either perform the sub query on the client side, as I resorted to doing, or storing the results of what would be the subquery in a manner that can be accessed through the main query (ie storing counts on the contact entity).
You could theoretically do this "on the fly" by making a SubQueryResult entity that stores a ContactId, and SubQueryId. You'd first pull back the contacts that have at least 1 car, and create a SubQueryResult record for each record, with it's contactId, and a single SubQueryId that is generated client side to tie them all together.
Then you'd do another query that says give me all the contacts that are in this SubQueryResult with this SubQueryId, that do not have any vehicles that aren't cars.
I could only assume that this wouldn't be any more efficient than performing the two separate queries and performing the filter client side. Although with the new ExecuteMultipleRequests in the new CRM release, it may be close.
I have resorted to pulling back all of my records in CRM, and performing the check on the client side since CRM 2011 doesn't support this via Query Expressions.
You could write two Fetch XML statements, one to return all contacts and the count of their vehicles, and another to return all contacts and the count of their cars, then compare the list on the client side. But once again, you're having to return every contact and filter it client side.
It's not tested but how about this query expression? I'm linking in the Vehicle entity as an inner join, requiring that it's a Car. I'm assuming that the field VehicleType is a String because I'm a bit lazy and don't want to test it (I'm typing this hardcore style, no compilation - pure brain work).
Optionally, you might want to add a Criteria section as well to control which of the Contact instances that actually get retrieved. Do tell how it went!
Sorry for the verbosity. I know you like it short. My brains work better when circumlocutory.
new QueryExpression
{
EntityName = "contact",
ColumnSet = new ColumnSet("fullname"),
LinkEntities =
{
new LinkEntity
{
JoinOperator = JoinOperator.Inner,
LinkFromEntityName = "contact",
LinkFromAttributeName = "contactid",
LinkToEntityName = "vehicle",
LinkToAttributeName = "contactid",
Columns = new ColumnSet("vehicletype"),
EntityAlias = "Vroom",
//LinkCriteria = { Conditions =
//{
// new ConditionExpression(
// "vehicletype", ConditionOperator.Equal, "car")
//} }
LinkCriteria = { Conditions =
{
new ConditionExpression(
"vehicletype", ConditionOperator.NotEqual, "truck")
} }
}
}
};
EDIT:
I've talk to my MVP Gustaf Westerlund and he's suggested the following work-around. Let me stress that it's not an answer to your original question. It's just a way to solve it. And it's cumbersome. :)
So, the hint is to add a flag in the Contact or Person entity. Then, every time you create a new instance of Vehicle, you need to fire a message and using a plugin, update the information on the first about the creation of the latter.
This has several drawbacks.
It requires us to do stuff.
It's not the straight-forward do-this-and-that type of approach.
Maintenance is higher for every new type of Vehicle one adds.
Buggibility is elevated since there are many cases to regard (what happens to the flagification when a Vehicle instance is reasigned, deleted etc.).
So, my answer to your question is changed to: "can't be done". This remains effective until (gladly) proven wrong by presented alternative solution. Duck!
Personally, I'd fetch (almost) everything and unleash the hounds of LINQ onto it. But I'd do that without smiling nor proud. :)