I'm using NetSweet/NetSuite Ruby gem for connecting to NetSuite accounts.
Normally it's trivial to get list of shipping methods using get_select_value. However one NetSuite account is organized into subsidiaries, and shipping methods are assigned to subsidiaries. For this particular account, get_select_value returns empty list:
NetSuite::Records::BaseRefList.get_select_value({
recordType: "salesOrder",
field: "shipMethod",
}).base_refs.length
=> 0
This makes some sense: in new sales order form the list of shipping methods is initially empty. When I select "customer" option, NetSuite automatically fills the read-only field "subsidiary". Then list of available shipping methods is populated.
Is there a way to fetch list of shipping methods by subsidiary or for a customer?
NetSuite API response contains a warning: "Results are incomplete. You must provide a value for field entity."
Full response:
<getSelectValueResponse xmlns="urn:messages_2016_2.platform.webservices.netsuite.com">
<platformCore:getSelectValueResult xmlns:platformCore="urn:core_2016_2.platform.webservices.netsuite.com">
<platformCore:status isSuccess="true">
<platformCore:statusDetail type="WARN">
<platformCore:code>WARNING</platformCore:code>
<platformCore:message>Results are incomplete. You must provide a value for field entity.</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
<platformCore:totalRecords>0</platformCore:totalRecords>
<platformCore:totalPages>0</platformCore:totalPages>
</platformCore:getSelectValueResult>
</getSelectValueResponse>
Using filterByValueList and passing customer internalId as entity seems to work.
NetSuite::Records::BaseRefList.get_select_value({
recordType: "salesOrder",
field: "shipMethod",
filterByValueList: {
"platformCore:filterBy" => {
"platformCore:field" => "entity",
"platformCore:internalId" => ns_customer.internal_id}
}
}).base_refs.length
=> 17
It's somewhat counter-intuitive, as filter option should reduce items in the output.
Generated XML:
<platformMsgs:getSelectValue>
<platformMsgs:pageIndex>1</platformMsgs:pageIndex>
<platformMsgs:fieldDescription>
<platformCore:recordType>salesOrder</platformCore:recordType>
<platformCore:field>shipMethod</platformCore:field>
<platformCore:filterByValueList>
<platformCore:filterBy>
<platformCore:field>entity</platformCore:field>
<platformCore:internalId>4978501</platformCore:internalId>
</platformCore:filterBy>
</platformCore:filterByValueList>
</platformMsgs:fieldDescription>
</platformMsgs:getSelectValue>
Related
I am trying to build a custom sorting for the product listings in shopware 6.
I want to include a foreign table (entity is: leasingPlanEntity), get the min of one of the fields of that table (period_price) and then order the search result by that value.
I have already built a Subscriber, and try it like that, what seems to work.
public static function getSubscribedEvents(): array
{
return [
//ProductListingCollectFilterEvent::class => 'addFilter'
ProductListingCriteriaEvent::class => ['addCriteria', 5000]
];
}
public function addCriteria(ProductListingCriteriaEvent $event): void
{
$criteria = $event->getCriteria();
$criteria->addAssociation('leasingPlan');
$criteria->addAggregation(new MinAggregation('min_period_price', 'leasingPlan.periodPrice'));
// Sortierung hinzufügen.
$availableSortings = $event->getCriteria()->getExtension('sortings') ?? new ProductSortingCollection();
$myCustomSorting = new ProductSortingEntity();
$myCustomSorting->setId(Uuid::randomHex());
$myCustomSorting->setActive(true);
$myCustomSorting->setTranslated(['label' => 'My Custom Sorting at runtime']);
$myCustomSorting->setKey('my-custom-runtime-sort');
$myCustomSorting->setPriority(5);
$myCustomSorting->setFields([
[
'field' => 'leasingPlan.periodPrice',
'order' => 'asc',
'priority' => 1,
'naturalSorting' => 0,
],
]);
$availableSortings->add($myCustomSorting);
$event->getCriteria()->addExtension('sortings', $availableSortings);
}
Is this already the right way to get the min(periodPrice)? Or is it taking just a random value out of the leasingPlan table to define the sort-order?
I didn't find a way, to define the min_period_price aggregate value in the $myCustomSorting->setFields Methods.
Update 1
Some days later, I asked a less complex question in the shopware community on slack:
Is it possible to use the DAL to define a subquery for an association in the product-listing?
It should generate something like:
FROM
JOIN (
SELECT ... FROM ... WHERE ... GROUP BY ... ORDER BY ...
) AS ...
The answer there was:
Don't think so
Update 2
I also did an in-deep anlysis of the DAL-Query-Builder, and it really seems to be not possible, to perform a subquery with the current version.
Update 3 - Different approach
A different approach might be, to define custom fields in the main entity. Every time a change is made on the main entity, the values of this custom fields should be recalculated.
It is a lot of overhead work, to realize this. Especially when the fields you are adding, are dependend on other data like the availability of a product in the store, for example.
So check, if it is worth the extra work. Would be better, to have a solution for building subqueries.
Unfortunately it seems that in your case there is no easy way to achieve this, if I understand the issue correctly.
Consider the following: for each product you can have multiple leasingPlan entities, and I assume that for a given context (like a specific sales channel or listing) that still holds. This means that you would have to sort the leasingPlan entities by price, then take the one with the lowest price, and then sort the products by their lowest-price leasingPlan's price.
There seems to be no other way to achieve that, and unfortunately for you, sorting is applied at the end, even if it is sort of a subquery.
So, for example, if you have the following snippet
$criteria = $event->getCriteria();
$criteria->addAssociation('leasingPlan');
$criteria->getAssociation('leasingPlan')
->addSorting(new FieldSorting('price', FieldSorting::ASCENDING))
->setLimit(1)
;
The actual price-sorting would be applied AFTER the leasingPlan entities are fetched - essentially the results would be sorted, meaning that you would not get the cheapest leasing plan per product, instead getting the first one.
You can only do something like that with filters, but in this case there is nothing to filter by - I assume you don't have one leasingPlan per SalesChannel or per language, so that you could limit that list to just one entry that could be used for sorting
That is not to mention that this could not be included in a ProductSortingEntity, but you could always work around that by plugging into the appropriate events and modifying the criteria during runtime
I see two ways to resolve your issue
Making another table which would store the cheapest leasingPlan per product and just using that as your association
Storing the information about the cheapest leasingPlans in e.g. cache and using that for filtering (caution: a mistake here would probably break the sorting, for example if you end up with too few or too many leasingPlans per product)
public function applyCustomSorting(ProductListingCriteriaEvent $event): void
{
// One leasingPlan per one product
$cheapestLeasingPlans = $this->myCustomService->getCheapestLeasingPlanIds();
$criteria = $event->getCriteria();
$criteria->addAssociation('leasingPlan');
$criteria->getAssociation('leasingPlan')
->addSorting(new FieldSorting('price', FieldSorting::ASCENDING))
->addFilter(new EqualsAnyFilter('id', $cheapestLeasingPlans))
;
}
And then you could sort by
$criteria->addSorting(new FieldSorting('leasingPlan.periodPrice', FieldSorting::ASCENDING));
There should be no need to add the association manually and to add the aggregation to the criteria, that should happen automatically behind the scenes if your custom sorting is selected in the storefront.
For more information refer to the official docs.
I'm new to graphQL and Hasura. I'm trying(in Hasura) to let me users provide custom aggregation (ideally in the form of a normal graphQL query) and have then each item the results compared against the aggreation.
Here's a example. Assume I have this schema:
USERTABLE:
userID
Name
Age
City
Country
Gender
HairColor
INCOMETABLE:
userID
Income
I created a relationship in hasura and I can query the data but my users want to do custom scoring of users' income level. For example, one user may want to query the data broken down by country and gender.
For the first example the result maybe:
{Country : Canada
{ gender : female
{ userID: 1,
Name: Nancy Smith,..
#data below is on aggregated results
rank: 1
%fromAverage: 35%
}...
Where I'm struggling is the data showing the users info relative to the aggregated data.
for Rank, I get the order by sorting but I'm not sure how to display the relative ranking and for the %fromAverage, I'm not sure how to do it at all.
Is there a way to do this in Hasura? I suspected that actions might be able to do this but I'm not sure.
You can use track a Postgres view. Your view would have as many fields as you'd like calculated in SQL and tracked as a separate "table" on your graphql api.
I am giving examples below based on a simplification where you have just table called contacts with just a single field called: id which is an auto-integer. I am just adding the id of the current contact to the avg(id) (a useless endeavor to be sure; just to illustrate...). Obviously you can customize the logic to your liking.
A simple implementation of a view would look like this (make sure to hit 'track this' in hasura:
CREATE OR REPLACE VIEW contact_with_custom AS
SELECT id, (SELECT AVG(ID) FROM contacts) + id as custom FROM contacts;
See Extend with views
Another option is to use a computed field. This is just a postgres function that takes a row as an argument and returns some data and it just adds a new field to your existing 'table' in the Graphql API that is the return value of said function. (you don't 'track this' function; once created in the SQL section of Hasura, you add it as a 'computed field' under 'Modify' for the relevant table) Important to note that this option does not allow you to filter by this computed function, whereas in a view, all fields are filterable.
In the same schema mentioned above, a function for a computed field would look like this:
CREATE OR REPLACE FUNCTION custom(contact contacts)
RETURNS Numeric AS $$
SELECT (SELECT AVG(ID) from contacts ) + contact.id
$$ LANGUAGE sql STABLE;
Then you select this function for your computed field, naming it whatever you'd like...
See Computed fields
I am building a messaging module into an existing web app. We are storing the messages in mongo with a data structure that something looks like:
{
id: "",
inResponseToMessageId: ""
to: []
cc: []
bcc: []
subject: ""
body: ""
owners: [{id:4, status:"read", folder:"inbox"}, {id:1, status:'unread', folder:'inbox'}]
dateSent:
}
The client would like us to be able to display messages in both a conversation view and a singleton view.
I am having trouble figuring out an efficient query that can
Return results grouped by message thread.
Work well with pagination.
Sortable by date and subject.
I can modify the data structure however I need in order to get this to work well.
Below are a few possible solutions but they all seem to fall short:
Store messages as children of a thread object.
Add a threadId to each message and then query and group by threadId.
Create some type of meta information object that helps.
The problem with the standard mongo group or $group function is that I imagine it will perform very poorly when the collection is large. We are expecting there to be hundreds of millions of messages in the collection.
Put a threadId on your messages.
Return results grouped by message thread.
You can find messages by thread like
db.messages.find({ "threadId" : id })
I don't think there's any need to group them in the sense of the $group operator.
Work well with pagination.
Pagination for the messages in what order? Pagination works well if you have a sort on a unique field. dateSent should be unique if you keep it to millisecond precision, so you can paginate on it.
// page 1
db.messages.find({ "threadId" : id }).sort({ "dateSent" : -1 }).limit(25)
// page 2
db.messages.find({ "threadId" : id, "dateSent" : { "$gt" : <25th date sent> } }).sort({ "dateSent" : -1 }).limit(25)
Sortable by date and subject.
Who sorts messages by subject? Anyway, this is just a matter of creating the right indexes if you want to retrieve messages in date or subject order. Depending on your requirements you might be doing this sorting for a client view, where it might not be necessary to have the database sort the results. The client could do it for the returned subset instead.
I have a repository method that gets all the Users from the database. Each User has a single child object called Profile and one or many child objects that fall under a List called Companies. This repository method has been tested, works fine and returns type IQueryable, which I am using as a base to later get narrowed down results once a query is triggered.
What I am trying to do is get a list of users from that repository method that have at least one associated company that has an ID that matches an element in an existing list called 'targetCompanyIDs'. The list is of type List<int> and the company's ID is also an int. Here is my attempt to generate that list of users:
List<User> usersData = rep.GetUsersProfilesAndCompanies().Where(u => targetCompanyIDs.Contains(u.CompanyStructures.ID)).ToList();
The error I get is that type List does not contain a definition for ID. Makes sense, right? So what I tried to do is treat the User's associated companies as some type of group or aggregate. Here is my second attempt:
List<User> usersData = rep.GetUsersProfilesAndCompanies().Where(u => targetCompanyIDs.Contains(u.CompanyStructures.Any(cs => cs.ID))).ToList();
What I am trying to say is, for any company the user has associated with it, does that company's ID match with the list targetCompanyIDs? If so, include the user on the list. Unfortunately this gives an 'invalid argument' error.
Is there any way in LINQ to query against multiple child elements like I am trying to do here?
Try this way :
List<User> usersData = rep.GetUsersProfilesAndCompanies()
.Where(u => u.CompanyStructures.Any(o => targetCompanyIDs.Contains(o.ID)))
.ToList();
I need a way to locate a Magento object my multiple attributes. I can look up an object by a single parameter using the 'loadByAttribute' method, as follows.
$mageObj->loadByAttribute('name', 'Test Category');
However, I have been unable to get this to work for multiple parameters. For example, I would like to be able to do the above query using all of the following search parameters. It might look something like the following.
$mageObj->loadByAttribute(array('entity_id' => 128,
'parent_id' => 1,
'name' => 'Test Category'));
Yes, I know that you don't need all of these fields to find a single category record. However, I am writing a module to export and import a whole website, and I need to test if an object, such as a category, already exists on the target system before I create it. To do this, i have to check to see if an object of the same type, with multiple matching attributes already exists, even if it's ID is different.
This may not answer your question, but it may solve your problem.
Magento does not support loadByAttribute for multiple attributes, but instead you can do this.
$collection = $mageObj->getCollection()
->addAttributeToFilter('entity_id', 128)
->addAttributeToFilter('parent_id', 1)
->addAttributeToFilter('name', 'Test Category');
$item = $collection->getFirstItem();
if ($item->getId()){
//the item exists
}
else {
//the item does not exist
}
addAttributeToFilter works for EAV entities (products, categories, customers).
For flat entities use addFieldToFilter.
There is a special case for sales entities (orders, invoices, creditmemos and shipments) that can use both of them.