Dynamics GP Web Service -- Returning list of sales order based on specific criteria - asp.net

For a web application, I need to get a list or collection of all SalesOrders that meet the folowing criteria:
Have a WarehouseKey.ID equal to "test", "lucmo" or "Inno"
Have Lines that have a QuantityToBackorder greater than 0
Have Lines that have a RequestedShipDate greater than current day.
I've succesfully used these two methods to retrieve documents, but I can't figure out how return only the ones that meet above criteria.
http://msdn.microsoft.com/en-us/library/cc508527.aspx
http://msdn.microsoft.com/en-us/library/cc508537.aspx
Please help!

Short answer: your query isn't possible through the GP Web Services. Even your warehouse key isn't an accepted criteria for GetSalesOrderList. To do what you want, you'll need to drop to eConnect or direct table access. eConnect has come a long way in .Net if you use the Microsoft.Dynamics.GP.eConnect and Microsoft.Dynamics.GP.eConnect.Serialization libraries (which I highly recommend). Even in eConnect, you're stuck with querying based on the document header rather than line item values, though, so direct table access may be the only way you're going to make it work.
In eConnect, the key piece you'll need is generating a valid RQeConnectOutType. Note the "ForList = 1" part. That's important. Since I've done something similar, here's what it might start out as (you'd need to experiment with the capabilities of the WhereClause, I've never done more than a straightforward equal):
private RQeConnectOutType getRequest(string warehouseId)
{
eConnectOut outDoc = new eConnectOut()
{
DOCTYPE = "Sales_Transaction",
OUTPUTTYPE = 1,
FORLIST = 1,
INDEX1FROM = "A001",
INDEX1TO = "Z001",
WhereClause = string.Format("WarehouseId = '{0}'", warehouseId)
};
RQeConnectOutType outType = new RQeConnectOutType()
{
eConnectOut = outDoc
};
return outType;
}
If you have to drop to direct table access, I recommend going through one of the built-in views. In this case, it looks like ReqSOLineView has the fields you need (LOCNCODE for the warehouseIds, QTYBAOR for backordered quantity, and ReqShipDate for requested ship date). Pull the SOPNUMBE and use them in a call to GetSalesOrderByKey.
And yes, hybrid solutions kinda suck rocks, but I've found you really have to adapt if you're going to use GP Web Services for anything with any complexity to it. Personally, I isolate my libraries by access type and then use libraries specific to whatever process I'm using to coordinate them. So I have Integration.GPWebServices, Integration.eConnect, and Integration.Data libraries that I use practically everywhere and then my individual process libraries coordinate on top of those.

Related

Dealing with numbers using Spring ExampleMatcher

I am new to Java and Spring, and I am building a sytem using Spring JPA. I am now working on my service and controller classes, and I would like to create a dynamic query. I have created a form, in which the user can enter values in the fields, or leave them blank. I then use example matcher to create an example based on non null fields and query objects in the database that match non null fields of the object.
It is working fine with Strings, and it works ok with numbers, in case the number entered by the user is matching the number in the database. What I would like to ask the community is: how can we, using Spring ExampleMatcher, add logic so that the query relating to numbers is not Select * from Projects where project.return = 10 but for instance Select * from Projects where project.return >=10?
It is a pretty basic question, but I have looked everywhere on the web, and I could not find an answer. All sources that I found said that ExampleMatcher deals only with Strings, but I find that strange that such a powerful system does not have a logic to deal with higherthan / lowerthan number type of criteria.
My code for the example matcher:
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreNullValues()
.withIgnoreCase()
.withIgnorePaths("projectId", "businessPlans", "projectReturn", "projectAddress.addressId")
I would like to add something like:
.withMatcher("projectAmountRaised", IsMoreThan(Long.parseLong()));
What I would have loved to have, but it is deprecated:
public static List getStockDailyRecordCriteria(Date startDate,Date endDate,
Long volume,Session session){
Criteria criteria = session.createCriteria(StockDailyRecord.class);
if(startDate!=null){
criteria.add(Expression.ge("date",startDate));
}
if(endDate!=null){
criteria.add(Expression.le("date",endDate));
}
if(volume!=null){
criteria.add(Expression.ge("volume",volume));
}
criteria.addOrder(Order.asc("date"));
return criteria.list();
}
I am thus looking for something similar... I could create a broad results list from just Strings criteria using ExampleMatcher, and then write my own logic to delete objects that do not fit number criteria, but I am sure there is a more elegant approach.
Thank you a lot for your help, and for your indulgence!
This is how you can use QBE and pageable with additional filters:
ExampleMatcher matcher = UntypedExampleMatcher.matching()
.withIgnoreCase()
.withIgnorePaths("startDate");
MyDao probe = new MyDao()
final Example<MyDao> example = Example.of(probe, matcher);
Query q = new Query(new Criteria().alike(example)).with(pageable);
q.addCriteria(Criteria.where("startDate").gte(probe.getStartDate()));
List<MyDao> list = mongoTemplate.find(q, example.getProbeType(), "COLLECTION_NAME");
PageableExecutionUtils.getPage(list, pageable, () -> mongoTemplate.count(q, example.getProbeType(), "COLLECTION_NAME"));

Storing timestamp in joining node value instead of Boolean in Firebase database

Say that I have node user, item and user_items used to join them.
Typically one would(as advised in official documents and videos) use such a structure:
"user_items": {
"$userKey": {
"$itemKey1": true,
"$itemKey2": true,
"$itemKey3": true
}
}
I would like to use the following structure instead:
"user_items": {
"$userKey": {
"$itemKey1": 1494912826601,
"$itemKey2": 1494912826602,
"$itemKey3": 1494912826603
}
}
with values being a timestamp value. So that i can order them by creation date also while being able to tell the associated time. Seems like killing two birds with one stone situation. Or is it?
Any down sides to this approach?
EDIT: Also I'm using this approach for the boolean fields such as: approved_at, seen_at,... etc instead of using two fields like:
"some_message": {
"is_seen": true,
"seen_timestamp": 1494912826602,
}
You can model your database in every way you want, as long as you follow Firebase rules. The most important rule is to have the data as flatten as possible. According to this rule your database is structured correctly. There is no 100% solution to have a perfect database but according to your needs and using one of the following situations, you can consider that is a good practice to do it.
1. "$itemKey1": true,
2. "$itemName1": true,
3. "$itemKey1": 1494912826601,
4. "$itemName1": 1494912826601,
What is the meaning of "$itemKey1": 1494912826601,? Beacause you already have set a timestamp, means that your item was uploaded into your database and is linked to the specific user, which means also in other words true. So is not a bad approach to do something like this.
Hope it helps.
Great minds must think alike, because I do the exact same thing :) In my case, the "items" are posts that the user has upvoted. I use the timestamps with orderBy(), along with limitToLast(50) to get the "last 50 posts that the user has upvoted". And from there they can load more. I see no downsides to doing this.

Meteor realtime game - match two players according to their score?

I want to build a realtime quiz game which randomly matches two players (according to their winning rate if they are logged in). I've read through the book Discover Meteor and have a basic understanding of the framework, but I just have no idea of how to implement the matching part. Anyone know how to do that?
if you want to match users who have scores close to each other, you can do something like this : mongodb - Find document with closest integer value
The Meteor code for those Mongo queries is very similar, but there are some subtle differences that are kind of tricky. In Meteor, it would look something like this :
SP // "selected player" = the User you want to match someone up with
var score = SP.score; // selected player's score
var queryLow = {score: {$lte:score},_id:{$ne:SP._id}};
var queryHigh = {score:{$gte:score},_id:{$ne:SP._id}};
// "L" is the player with the closest lower score
var L=Players.findOne(queryLow,{sort:{score:-1},limit:1});
// "H" is the player with the closest higher score
var H=Players.findOne(queryHigh,{sort:{score:1},limit:1});
so, now you have references to the players with scores right above and right below the 'selected player'. In terms of making it random, perhaps start with a simple algorithm like "match me with the next available player who's score is closest" , then if it's too predictable and boring you can throw some randomness into the algorithm.
you can view the above Meteor code working live here http://meteorpad.com/pad/4umMP4iY8AkB9ct2d/ClosestScore
and you can Fork it and mess about with the queries to see how it works.
good luck! Meteor is great, I really like it.
If you add the package peppelg:random-opponent-matcher to your application, you can match together opponents like this:
On the server, you need to have an instance of RandomOpponentMatcher like this:
new RandomOpponentMatcher('my-matcher', {name: 'fifo'}, function(user1, user2){
// Create the match/game they should play.
})
The function you pass to RandomOpponentMatcher will get called when two users been matched to play against each other. In it, you'll probably want to create the match the users should play against each other (this package does only match opponents together, it does not contain any functionality for playing games/matches).
On the client, you need to create an instance of RandomOpponentMatcher as well, but you only pass the name to it (the same name as you used on the server):
myMatcher = new RandomOpponentMatcher('my-matcher')
Then when the users is logged in and which to be matched with a random opponent, all you need to do is to call the add method. For example:
<template name="myTemplate">
<button class="clickMatchesWithOpponent">Match me with someone!</button>
</template>
Template.myTemplate.events({
'click .clickMatchesWithOpponent': function(event, template){
myMatcher.add()
}
})
When two different logged in users has clicked on the button, the function you passed to RandomOpponentMatcher on the server will get called.
One implementation might be as follows:
A user somehow triggers a 'looking for game' event that sets an attribute on user.profile.lookingForGame to true. The event then makes a call to a server side Meteor method which queries for all other online users looking for games.
From there you it really depends on how you want to handle users once they 'match'.
To determine all online users, try using the User Status package:
https://github.com/mizzao/meteor-user-status
Once added, any online user will have an attribute in the profile object of 'online'. You can use this to query for all online users.

sort Ektron content types

Ektron 801 SP1
I am using the following code to fetch some smart form content. Can I pre-sort (using OrderByField?) before I fetch 20 rows? I am sorting memberlist but that is after the fact and kinda useless. What am I missing?
Criteria<ContentProperty> criteria1 = new Criteria<ContentProperty>();
criteria1.AddFilter(ContentProperty.XmlConfigurationId, CriteriaFilterOperator.EqualTo, MEMBERS_ID);
criteria1.PagingInfo = new PagingInfo(20);
List<ContentType<member>> memberslist = contentTypeManager.GetList(criteria1);
I have good news and bad news for you.
First, the good news. You can sort by Content Properties with the Criteria object before you pull the 20 items. You'll want to use the OrderByField and OrderByDirection properties of the criteria.
criteria.OrderByField = ContentProperty.DateCreated;
criteria.OrderByDirection = EkEnumeration.OrderByDirection.Descending;
The bad news comes when trying to order items based on fields within the Smart Form. You might be able to do so using the IndexSearch API, but since Ektron 8.0* still relies on Microsoft's Indexing Service, I'm not a fan of that approach and don't have any code to share. If you choose to go that route, the premise is to use search to return the content IDs in the correct order, then use the criteria, as you are, to get items with those IDs.
What you can do with just the API is use Microsoft LINQ to sort the data after it's loaded, but in order to get the right results in the right order you have to load all items first (and ideally cache them to minimize performance impact). I'm using one of my content types as an example, but you should get the idea.
var membersList = new List<SlideBannerType>();
var sortedList = membersList.OrderBy(s => s.EnableAlternateText);
var firstpage = sortedList.Take(20);
var nextpage = sortedList.Skip(20).Take(20);
It's not ideal, but it does work very well for smaller (in the hundreds, perhaps thousands, but not tens of) data sets.
The second bit of good news, though, is that Ektron uses Microsoft Search Server for versions 8.5 and up. This has a much, much more robust API and performs fantastic (both in terms of speed and reliability). The premise would actually stay the same as that for the IndexSearch, use Search to get the IDs in the right order, and then ContentManager (or ContentTypeManager) to get the items. I've used this approach several times, albeit not with Smart Forms specifically. Your best result would come from upgrading to 8.6 and Microsoft Search Server and using the two APIs together to get each page of data. In doing so, it would actually be almost trivial at that point to mix in advanced search and filter options as well with the new search APIs.

InfoPath autonumber field

I am designing an infopath (Change Request) form:
1)How can i add a text box that automaticaly increments to the next number when a new form is created (adding a new Change Request form to the form library).
2)How do i retrieve information from an existing form to the new form.
NOTE: The field is not inside a repeating table. I need to generate the next Change Request number on each new Change Request form.
TIA!
There is no build-in way to do this, but there are several ways to achieve what you want (Database query or SPList query). But this kind of request somehow smells like a workaround for an other problem.
Common cases for increasing numbers are:
unique IDs
count the Requests
make referable by external list (same as ID)
make IDs guessable (time stamps are not)
If you need an ID: In most cases you are not forced to use integer IDs. Simply use the form title as a natural ID. (e.g. customer + timestamp)
If you need guessable IDs, you need them because an external system wants to access or refer to the request. In that case try to change the pull-direction into a push-direction (e.g. by using workflows) or let your other system provide a "getID" function that can be called by your form to obtain a known ID (no guessing needed).
Anyway - for me, it looks like you want to achieve this to solve some other problem. Maybe there are different solutions for that problem too?
You could enter a token in your text-titles on the form where you want autonumbering, such as #num#, and then use javascript or jquery to find those tokens and replace them with incremented numbers.
The drawback to this is that if you exported the list to excel, the tokens would not get translated to numbers. But it is a good solution for on-screen rendering.
Use Firebug to figure out the class of the container housing your autonumber tags.
Maybe you could do something like this:
function TokenReplacement(){
var ClassName = 'ms-formlabel';
var elements = new Array();
var elements = document.getElementsByTagName('td');
var numerator=0;
//Now do find and replace on everything else
for(var e=0;e<elements.length;e++){
thiselement = elements[e];
if(thiselement.className == ClassName){
//autonumber the questions by replacing the #num# token
if(thiselement.innerHTML.search('#num#') > -1){
numerator++
var replacenum = "<b>" + numerator + ". </b>";
thiselement.innerHTML = elements[e].innerHTML.replace('#num#',replacenum);
}
}
}
}

Resources