Finding the Total Available and Available physical for an Item/Warehouse - axapta

I have a method for a display field which does the following;
return InventSum::find(_salesLine.ItemId, InventDim::_salesLine.InventDimId).AvailPhysical();
This gives me the on-hand Available Physical for the line site/warehouse/location.
I need to see the total available for just the site/warehouse. I think I need to search inventDim by Item/Warehouse to get my inventdimid, but I cannot find the method so I am suspicious that this is incorrect.
Can anyone help?

My working solution...
InventDimParm invDimParm;
InventDim warehouseInvDim;
InventDim salesLineInventDim;
;
salesLineInventDim = _salesLine.inventDim();
warehouseInvDim.InventSiteId = salesLineInventDim.InventSiteId;
warehouseInvDim.InventLocationId = salesLineInventDim.InventLocationId;
warehouseInvDim = InventDim::findOrCreate(warehouseInvDim);
invDimParm.initFromInventDim(InventDim::find(warehouseInvDim.inventDimId));
return InventSum::findSum(_salesLine.ItemId,warehouseInvDim,invDimParm).availOrdered();
I know this is for availOrdered() but it works exactly the same for availPhysical()

You should use the InventOnhand class.
It sums the invent on-hand values based on criteria like item id and inventory dimensions.
There are lots of uses in AX, search the Class node.

The following job finds all Sales Lines with the Open Order status, which have an Available Physical quantity on hand matching all dimensions specified on the Sales Lines except location:
static void FindOpenSalesLineAvailPhys(Args _args)
{
SalesLine salesline;
InventDim inventDim;
InventDimParm inventDimParm;
InventOnHand inventOnHand;
;
while select salesLine where salesLine.SalesStatus == SalesStatus::Backorder
{
inventDim = salesLine.inventDim();
inventDimParm.initFromInventDim(inventDim);
inventDimParm.WMSLocationIdFlag = NoYes::No;
inventOnHand = InventOnHand::newItemDim(salesLine.ItemId, inventDim, inventDimParm);
if (inventOnHand.availPhysical())
{
info(strfmt("Sales Order %1 Line %2 Item Id %3 Available Physical (ignoring Location) %4",
salesLine.salesId, salesLine.LineNum, salesLine.ItemId, inventOnHand.availPhysical()));
}
}
}

You basically set your inventDim values the way you want to search for them, and then do an InventDim::FindOrCreate to see if either the inventory dimension already exists, or it needs to be created and a new number sequence will be consumed. This is used so that the InventDim table doesn't store every single possible combination of dimensions. Also because if you have any serialized products, it's not feasible for the table to store all of the combinations, so it only stores the ones it needs.
InventDim inventDim;
SalesLine _salesLine;
;
inventDim.InventSiteId = 'mySite';
inventDim.InventLocationId = 'myWarehouse';
inventDim = InventDim::findOrCreate(inventDim);
return InventSum::find(_salesLine.ItemId, inventDim.inventDimId).AvailPhysical();

Related

Set default data in field x++ PurchCreateOrder

I want to set a default value based on curUserid() in PurchCreateOrder. How can I put data in my field on form extension ?
Is there any better option of doing this ? Fields are bound to datasource and I have different fields with differentdatasources.
My code is giving me abnormal termination error with nullreferences. XPPCompiler
[ExtensionOf(tableStr(PurchTable))]
final class PurchCreateOrderGetDefAdress_Extension
{
void initvalue(PurchaseType _purchaseType)
{
next initvalue(_purchaseType);
PurchTable purchtable;
LogisticsPostalAddress logpostadress;
UserInfoSz usrsz;
str user = curUserId();
select firstonly logpostadress where logpostadress.city == 'lub';
// select firstonly InventSiteId, InventLocationId from purchtable join usrsz where purchtable.InventSiteId == usrsz.InventSiteId && usrsz.IsDefault == true;
select firstonly InventSiteId from usrsz where usrsz.UserId == user && usrsz.IsDefault == true;
purchtable.initValue();
purchtable.deliveryname = 'asasasasas' ;//logpostadress.Address;
purchtable.inventsiteid = usrsz.InventSiteId;
purchtable.inventlocationid = usrsz.InventSiteId;
info(strFmt("%1, %2, %3", logpostadress.Address, usrsz.InventSiteId));
}
}
The error is straight forward.
Error The augmented class 'PurchTable' provides a method by this name, but this method cannot be used as a chain of command method since the parameter profile does not match the original method.
Take a look at the highlighted parameter profile and compare to yours.
Edit: Take a look at these links for more info on Chain of Command (CoC):
https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/extensibility/method-wrapping-coc
https://channel9.msdn.com/Blogs/mfp/X-Chain-Of-Command (This video is excellent)

AX NumberSequence: Mark a number as used

How to mark a given number as used in a NumberSequence when this number was not generated by the number sequence?
Let's consider I imported the first 10 records of a custom table and the file already specified its ID from 01 to 10,
then i want to intercept insert() mark the given number as used so that after importing, the first manually created record will assign ID 11.
This would be something like updating the field 'Next' in the NumberSequence.
Update the NumberSequenceTable.NextRec value to the desired value.
Make sure that format is correct.
Example code:
NumberSequenceTable numberSequenceTable;
ttsBegin;
select forUpdate numberSequenceTable
where numberSequenceTable.NumberSequence == 'Acco_1' // as example
;
numberSequenceTable.NextRec = 11;
if (numberSequenceTable.validateField(fieldNum(NumberSequenceTable, NextRec))
&& numberSequenceTable.validateWrite()
)
{
numberSequenceTable.update();
}
else
{
throw error("Validation failed");
}
ttsCommit;

Why is my query so slow?

I try to tune my query but I have no idea what I can change:
A screenshot of both tables: http://abload.de/image.php?img=1plkyg.jpg
The relation is: 1 UserPM (a Private Message) has 1 Sender (User, SenderID -> User.SenderID) and 1 Recipient (User, RecipientID -> User.UserID) and 1 User has X UserPMs as Recipient and X UserPMs as Sender.
The intial load takes around 200ms, it only takes the first 20 rows and display them. After this is displayed a JavaScript PageMethod gets the GetAllPMsAsReciepient method and loads the rest of the data
this GetAllPMsAsReciepient method takes around 4.5 to 5.0 seconds each time to run on around 250 rows
My code:
public static List<UserPM> GetAllPMsAsReciepient(Guid userID)
{
using (RPGDataContext dc = new RPGDataContext())
{
DateTime dt = DateTime.Now;
DataLoadOptions options = new DataLoadOptions();
//options.LoadWith<UserPM>(a => a.User);
options.LoadWith<UserPM>(a => a.User1);
dc.LoadOptions = options;
List<UserPM> pm = (
from a in dc.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending select a
).ToList();
TimeSpan ts = DateTime.Now - dt;
System.Diagnostics.Debug.WriteLine(ts.Seconds + "." + ts.Milliseconds);
return pm;
}
}
I have no idea how to tune this Query, I mean 250 PMs are nothing at all, on other inboxes on other websites I got around 5000 or something and it doesn't need a single second to load...
I try to set Indexes on Timestamp to reduce the Orderby time but nothing happend so far.
Any ideas here?
EDIT
I try to reproduce it on LinqPad:
Without the DataLoadOptions, in LinqPad the query needs 300ms, with DataLoadOptions around 1 Second.
So, that means:
I could save around 60% of the time, If I can avoid to load the User-table within this query, but how?
Why Linqpad needs only 1 second on the same connection, from the same computer, where my code is need 4.5-5.0 seconds?
Here is the execution plan: http://abload.de/image.php?img=54rjwq.jpg
Here is the SQL Linqpad gives me:
SELECT [t0].[PMID], [t0].[Text], [t0].[RecieverID], [t0].[SenderID], [t0].[Title], [t0].[Timestamp], [t0].[IsDeletedRec], [t0].[IsRead], [t0].[IsDeletedSender], [t0].[IsAnswered], [t1].[UserID], [t1].[Username], [t1].[Password], [t1].[Email], [t1].[RegisterDate], [t1].[LastLogin], [t1].[RegisterIP], [t1].[RefreshPing], [t1].[Admin], [t1].[IsDeleted], [t1].[DeletedFrom], [t1].[IsBanned], [t1].[BannedReason], [t1].[BannedFrom], [t1].[BannedAt], [t1].[NowPlay], [t1].[AcceptAGB], [t1].[AcceptRules], [t1].[MainProfile], [t1].[SetShowHTMLEditorInRPGPosts], [t1].[Age], [t1].[SetIsAgePublic], [t1].[City], [t1].[SetIsCityShown], [t1].[Verified], [t1].[Design], [t1].[SetRPGCountPublic], [t1].[SetLastLoginPublic], [t1].[SetRegisterDatePublic], [t1].[SetGBActive], [t1].[Gender], [t1].[IsGenderVisible], [t1].[OnlinelistHidden], [t1].[Birthday], [t1].[SetIsMenuHideable], [t1].[SetColorButtons], [t1].[SetIsAboutMePublic], [t1].[Name], [t1].[SetIsNamePublic], [t1].[ContactAnimexx], [t1].[ContactRPGLand], [t1].[ContactSkype], [t1].[ContactICQ], [t1].[ContactDeviantArt], [t1].[ContactFacebook], [t1].[ContactTwitter], [t1].[ContactTumblr], [t1].[IsContactAnimexxPublic], [t1].[IsContactRPGLandPublic], [t1].[IsContactSkypePublic], [t1].[IsContactICQPublic], [t1].[IsContactDeviantArtPublic], [t1].[IsContactFacebookPublic], [t1].[IsContactTwitterPublic], [t1].[IsContactTumblrPublic], [t1].[IsAdult], [t1].[IsShoutboxVisible], [t1].[Notification], [t1].[ShowTutorial], [t1].[MainProfilePreview], [t1].[SetSound], [t1].[EmailNotification], [t1].[UsernameOld], [t1].[UsernameChangeDate]
FROM [UserPM] AS [t0]
INNER JOIN [User] AS [t1] ON [t1].[UserID] = [t0].[RecieverID]
WHERE ([t0].[RecieverID] = #p0) AND (NOT ([t0].[IsDeletedRec] = 1))
ORDER BY [t0].[Timestamp] DESC
If you want to get rid of the LoadWith, you can select your field explicitly :
public static List<Tuple<UserPM, User> > GetAllPMsAsReciepient(Guid userID)
{
using (var dataContext = new RPGDataContext())
{
return (
from a in dataContext.UserPMs
where a.RecieverID == userID
&& !a.IsDeletedRec
orderby a.Timestamp descending
select Tuple.Create(a, a.User1)
).ToList();
}
}
I found a solution:
At first it seems that with the DataLoadOptions is something not okay, at second its not clever to load a table with 30 Coloumns when you only need 1.
To Solve this, I make a view which covers all nececeery fields and of course the join.
It reduces the time from 5.0 seconds to 230ms!

Technique to reduce load on server,while searching & comparing for particular item into large(10,000 entries) LIST object created at server side

I want to know that,
If we have LIST object created at server side which contains large amount of data entries like employess master data(10,000), & I want to give search option to search valid employee ID or name.
So I have tried to compare that entered text with that list of large entries in loop, which is obvious degrading performance.
So is there any option to better performace?
Thanks in advance.
Try this:
public List<Employee> SearchEmployee(string search, int pageNo, int pageLength)
{
MasterDataContext db = new MasterDataContext();
var searchResult = (from e in db.Employess
where (search == null ||
e.Name.ToLower().Contains(search.ToLower()))
select e).ToList();
int pageStart = (pageNo - 1) * pageLength;
var pageResult = from c in searchResult.Skip(pageStart).Take(pageLength)
orderby c.CardNo
select c;
return pageResult;
}
I hope it helps.

Generate customer price list

I have a requirement for the generation of a full customer by item price list to be exported to a 3rd party program, taking into account trade agreements / discounts etc.
To get the list of customers by items I have tried:
creating a double loop (eg outer loop of all customers, inner loop with all items)
creating a select statement that joins the custTable to inventTable
To generate a price I have been creating a fake SalesLine for the given customer / item and executing salesLine.calcLineAmount(1) in x++
However this takes ~6 hours to process the full customer / item list.
The only other thing I have though of is to run this process once and store in a table, then any time a price / trade agreement / discount is changed, only update the relevant records.
Does anyone have any other suggestions of a better way to achieve this result?
How did you join CustTable to InventTable? That's just a Cartesian join I believe, which is all possible customers against all possible items, which is inherently slow anyway.
And the nature of pricing/trade agreements/discounts is that they change frequently and it's not really practical to keep a running table of what every price for every customer is, especially if you have trade agreements for date ranges, specific quantity thresholds or units, warehouse it came out of, etc.
Here is code to do what you want and I believe it'd work faster than whatever you're doing. I quickly wrote this, but it should work and you should remove the integer breaks, I just didn't want it to run forever.
static void Job66(Args _args)
{
PriceDisc priceDisc;
container retVal;
CustTable custTable;
InventTable inventTable;
InventTableModule inventTableModule;
int i, n;
;
while select custTable
{
i++;
if (i>5)
break;
n = 0;
while select inventTable
join inventTableModule
where inventTableModule.ItemId == inventTable.ItemId &&
inventTableModule.ModuleType == ModuleInventPurchSales::Sales
{
n++;
if (n>10)
break;
retVal = priceDisc::findItemPriceAgreement(ModuleInventPurchSales::Sales,
inventTable.ItemId,
InventDim::findOrCreateBlank(false),
inventTableModule.UnitId,
SystemDateGet(),
1,
custTable.AccountNum,
custTable.Currency,
custTable.PriceGroup);
info(strfmt("%1 - %2 - %3 - %4 - %5 - %6 - %7 - %8 - %9", custTable.AccountNum,
custTable.Name,
inventTable.ItemId,
inventTable.ItemName,
conPeek(retVal, 1), // priceDisc.price(),
conPeek(retVal, 2), // priceDisc.markup(),
conPeek(retVal, 3), // priceDisc.priceUnit(),
conPeek(retVal, 4), // priceDisc.deliveryDays(),
conPeek(retVal, 5))); // priceDisc.calendarDays()];
}
}
}

Resources