Cleaning up unused Customers - axapta

I am being asked to clean unused Customers in our AX database. The challenge is defining "unused" of course.
Is there any diagram anywhere documenting the table's relationship in Dynamics AX 2009 ?
For example, I see the table LedgerJournalTrans has the "AccountNum" field. I guess I could extrapolate that if a Customer has no associated records in LedgerJournalTrans, it is unused but I think it may be a bit more complicated than this.
Anything else I should watch for ?
Thanks!

I have had to do this before, and it really isn't terribly challenging, you just have to do your due diligence. I wouldn't feel as confident with ian_scho's method because it only checks two tables, but I'd say his method gets the 80%.
I would say your best method is to copy the class Classes\InventUnusedDimCleanUp and modify the simple functions in the \run method.
This is a base class that basically does the following, except I'm going to substitute InventDimId with AccountNum for solidarity:
Insert every customer account (AccountNum) into an empty check table as a starting reference
Traverse the Data Dictionary\Tables tree node over every table in the AOT
While traversing, determine if the table is a candidate table to compare against
If it is a candidate table, then traverse each field and determine if it's a candidate field by checking if it is an EDT of CustAccount or an EDT that extends it
If we determine it is a candidate field, then insert that into our container list of [TableId,FieldId]s
Next, loop through the container and for each tableId/fieldId, delete from our check table AccountNum's that DO exist in the candidate tables, so that we will be left with a check table of AccountNum's that were not found in any table
Lastly, a step you will probably do manually, but it will delete from CustTable the customers that are remaining in the check table, which have been deemed unused
This should accomplish your task, but doesn't take in account any external systems or customization you may have...but it gets the 95%.

Don't forget customer transactions - In one company or across all of them?
And in AX 2012 the financial dimension framework may reference the customer table itself.
CustTable custTable;
CustTrans custTrans;
//DefaultDimensionView defaultDimensionView; // **AX 2012**
;
//Customer transactions not found.
setPrefix("#SYS119665");
while select crossCompany AccountNum, Party from custTable
notExists join custTrans
where custTable.AccountNum == custTrans.AccountNum
{
info (strFmt("%1 - %2", custTable.AccountNum, custTable.name()));
//// Check financial dimension definition, 'Customer' **AX 2012**
//select firstonly defaultDimensionView
// where defaultDimensionView.DisplayValue == custTable.AccountNum
// && defaultDimensionView.Name == "Customer"; //"Client" in some countries? May depend upon AX installation.
//if (defaultDimensionView)
//{
// warning("Financial dimension value exists.");
//}
}
Personally I'd advise against deleting ANYTHING on these systems. Produce a report about what percentage of master data is being used, and if the data negatively impacts on usability...
Then see if your boss forgets about the request :)

Related

Iteration over VendTransOpen

This happens in Accounts Payable -> Journals -> Payments -> Payment Journal.
I choose to see the Lines for a journal and from Functions I select Settlement. I am not sure if this is the same for everyone else.
So, when clicking Settlement, VendOpenTrans opens. I need to iterate over it, and Mark the records according to the invoice of the previously selected LedgerJournalTrans field.
First of all I have to check the VendOpenTrans fields which I am not able to accomplish.
I have added the following piece of code in the init of VendTransOpen:
VendTrans vt;
vt = vendTransOpen_ds.getFirst(true) as VendTrans ;
while (vt)
{
//Do your thing
vt= vendTransOpen_ds.getNext() as VendTrans ;
}
No elements seem to be present in the vendTransOpen_ds..
Can someone give me a hint about this?
Update 1:
Found this :
Understanding the Settlement Mechanism in Microsoft Dynamics AX
and
Automatic mark a Settlement Transactions on a Payment Journal in AX 2012
I didn't think it would be so damn difficult.. I will start digging tomorrow.
Several things are wrong, but probably my #2 is your main problem.
If you place this code in the init method, the query hasn't been executed yet, so nothing will be there. See https://msdn.microsoft.com/en-us/library/aa608211.aspx
Your code will never enter while (vt) because vt will never have a value as written because VendTrans and VendTransOpen are two different tables that don't support inheritance.
The only reason vt = vendTransOpen_ds.getFirst(true) as VendTrans ; doesn't throw an error is because FormDataSource.getFirst()/getNext() returns a Common table record.
What Jan said too.
First off, use getFirst(0) before using getNext().
The zero indicates you want all records rather than marked.
Search, use cross reference tool, or google to get lots of references for the use of these functions.

Microsoft Dynamics AX 2012 - Purchase Price

I've been instructed to create a customization on how the Unit Price (Purchline.PurchPrice) is calculated.
Right now when I'm creating a new purchase order and select an item, it simply pulls from the pricing from the released products for that particular item.
For my customization, I'm going to be using 3 variables to determine the pricing.
1: ItemId
2: Current Session Date
3: Customized Field in the Purchase Header
As such, I'll need access to purchline for the ItemId on the current line, and access to purchtable to access my field in the header.
Right now there is a big process for how the pricing gets pulled from released products, how the system checks for discounts, etc.
My question is, can anyone suggest the best class/location to check and modify where my final PurchPrice field gets set and inserted into purchline?
I need this to be basically the last part of the process of how this PurchPrice gets calculated. I've looked around in the PriceDisc & PriceConvert classes, SalesPurchLine map, the modified method of the ItemId field of the form.
AxPurchline doesn't seem to be triggering at all when I put breakpoints in them and create new purchase order lines.
Any help, insight or advice on where it would be the best to make logic changes for the PurchPrice field would be greatly appreciated.
Thanks in advance!
The table Purchline actually has a method called setPriceDisc where the price agreement is set and the line amount is adjusted.
This seems to be the last place where PurchLine.PurchPrice is set.

Get all addresses on the system Dynamics AX 2012

QUESTION:
I would like to know how can I get all the addresses on the system, including (customers, vendors, banks and tax authorities) on a query, and put it on a view with columns (name, address).
So it would be parties and non-parties entities.
WHAT I'VE TRIED TO DO:
I use the table concepts to figure out the problem for parties:
LogisticsPostalAddress - table contains addresses in the system that could be associated with various entities.
Is where the main information about the addresses are, it seems that the rest are extra descriptions of these addresses.
The CustTable and VendTable have a FK to DirPartyTable.
The BankAccountTable and the TaxAuthorityAddress have the relation directly with the LogisticsLocation that have relation with the LogisticsPostalAddress, at the relation Location_FK.
The question now is how to build the query that gets the addresses from Customers, BankAccounts, Vendors and Tax Authorities and put it on a view with (name,address) pair?
EDITED
I have found a way to get all addresses using a method, but not using a query, and is not very efficient, but it was enough for what I wanted:
1) Create a table: AllAddresses for example:
With fields: Name, Address
2) Create a job:
static void GettingAllAddresses(Args _args)
{
CustTable custTable;
VendTable vendTable;
TaxAuthorityAddress taxAuthorityAddress;
LogisticsPostalAddress logisticsPostalAddress;
AllAddresses allAddresses;
;
while select * from custTable
{
allAddresses.Address = custTable.address();
allAddresses.Name = custTable.name();
allAddresses.CustAccount = custTable.AccountNum;
allAddresses.doInsert();
}
//Getting all vendors
while select * from vendTable
{
allAddresses.Address = vendTable.address();
allAddresses.Name = vendTable.name();
allAddresses.doInsert();
}
//Getting all tax authorities addresses
while select * from taxAuthorityAddress
{
allAddresses.Address = TaxAuthorityAddress::address(taxAuthorityAddress.TaxAuthority);
allAddresses.Name = taxAuthorityAddress.Name;
allAddresses.doInsert();
}
//Getting all bank addresses
while select * from bankAccountTable
{
logisticsPostalAddress = LogisticsLocationEntity::location2PostalAddress(bankAccountTable.Location);
allAddresses.Address = logisticsPostalAddress.Address;
allAddresses.Name = bankAccountTable.Name;
allAddresses.doInsert();
}
}
Look at how the view LogisticsEntityPostalAddressView is built, it may already provide the information you need. If the Description field from the LogisticsLocation table isn't good enough and you really need the Name field from, for example, the table BankAccountTable, you will need to build a similar view.
You can see that view uses the view LogisticsEntityLocationView, which has a union query which pulls data from several tables. You could build a similar view to pull the Name field from BankAccountTable, DirPartyTable, and TaxAuthorityAddress.
As one can tell from your database diagram the relationship for the Global Address Book is very complex (and in many places really non-intuitive). One thing it appears you did not address is whether or not the LogisticsPostalAddress you get is active or not (as determined by the ValidFrom and ValidTo dates). AX has code that preserves the current state of an address that is changing in LogisticsPostalAddress by simply setting the ValidTo to 2 seconds before the change is committed, and creates a new LogisticsPostalAddress record with ValidTo set to the commit date so it will be active. If you have not had a lot of changes made to addresses then you probably won't have much of a problem, but if there were a lot you may get a lot of un-wanted address records. There is an article here http://hyperdaptive.com/2016/04/320/ that has some details in it that could be helpful - it also has some X++ and SQL code that could be very helpful if you happen to still be working on this. Good luck.

Qt: QSqlTableModel + QTableView sync with PostgreSQL

I'm writing a database access app for storing some data and want to ask a few questions about the model/view architecture.
(Using: Qt 4.7.4, own build; PostgreSQL 9.0; Targets: WinXP, Win7 (32/64 bit))
Let me first explain what I am trying to achieve and where I am currently.
I have two pages (subclassed QWidgets inserted in a QStackedWidget) with a QTableView bound to a model. Each view is bound to a table in the PostgreSQL server. You can add/edit/delete/sort/filter items.
Each page can be seen by only one type of users, lets call the roles Role1 and Role2.
The submit strategies of everything connected to the model are OnManualSubmit.
(Transaction isolation level = Serializable.) When two users want to edit(for example) the same row, I want to do a "SELECT ... FOR UPDATE" query - to make sure that when someone edits something, he will merge his changes with newer ones (if any, just like in SVN for example). But I see only a submitAll() method the QSqlTableModel.
Maybe catching the signals beforeUpdate(), beforeDelete(), beforeInsert() and performing manually "SELECT ... FOR UPDATE" is one option.
The other way I think is to subclass QSqlTableModel. What is the clean and nice way to achieve this?
I want to periodically update the QSqlTableView for each of the pages (one page is seen at most, Role1 users have access only to Page1 and the same for Role2 => Page2).
The first thing that came to my mind is to use a QTimer and manually call select() of the QSqlTableModel, but... not sure if this is the cool way.
I also want to periodically check if the connection to the database is ok, but I think that a QTimer + QSqlDatabase::isOpen () will do.
Now, the 2 tables have the same primary keys and some columns are the same. I want when a user with Role1 changes a row in Table1 to automatically change corresponding columns of Table2 and vice versa. Should I create a trigger in Postgres?
BTW, the database is small - each of the two tables is around 3-4000 rows with ~10 columns (varchars mostly, 1 text and 2 date colunms).
Thanks for reading and Happy New Year! :)
I think you should consider doing something of the following:
Instead of using QSqlTableModel as a model I'd implement my own model as a subclass of QAbstractTableModel. This will allow you a lot of control over what you can do in terms of data manipulation.
One thing that this will require is for certain fields in the table you would need to implement subclass of QAbstractItemDelegate that will allow for modification of data in the table as I am fairly sure you don't want to allow users updating any field in the table as for example primary key is likely have to be left alone.
For question 2 I would suggest implementing a field called transaction_counter for every row so you don't have to select every row in the table just the updated ones the transaction_counter will be updated on every row update and the new one will be inserted on the new row insert. One thing that will be required is that the counter is unique across the table. For example if initial state of the table is: row1 has counter = 0 and row2 has counter = 0. If row1 is updated counter set to 1. When row1 is then updated again counter on it is set to 2. When row2 is now updated counter on it is set to 3, etc. You can certainly do the data refreshes now using QTimer and this will be much more advantageous to for example checking the data as one user may be updating the same table as another user with the same Role.
For Question 3. I don't see any reason why not custom models and especially if you decide to separate data from the model you can manipulate data separately from it's display. Sort of Data->Model->View->Controller implementation. Each one can be maintained separately as long as you have a feedback mechanism for your delegates.
For Question 4. The answer is sure or you can implement the trigger in your application.
Hope this helps. Have a great New Year!

After join, cannot filter by attribute qty - getting products from inventory that are in stock

You have been so helpful in the past that I keep coming back searching for help and learning.
This time I am trying to get all products that have a quantity greater than 1 and that are in stock (is_in_stock = 1)
$products = Mage::getModel('catalog/product')->getCollection();
//$products->addAttributeToSelect('*');
//SELECT `e`.*, `stock`.`qty` FROM `catalog_product_entity` AS `e` LEFT JOIN `cataloginventory_stock_item` AS `stock` ON stock.product_id = e.entity_id
$products->getSelect()->joinLeft(
array('stock'=>'cataloginventory_stock_item'),
'stock.product_id = e.entity_id',
array('stock.qty', 'stock.is_in_stock')
);
This returns qty and is_in_stock columns attached to the products table. You can test it as follows:
$products->getFirstItem()->getQty();
$products->getFirstItem()->getIsInStock();
The issue begins when I try to filter by qty and is_in_stock.
$products->addFieldToFilter(array(
array('Qty','gt'=>'0'),
array('Is_in_stock','eq'=>'1'),
));
This returns - Invalid attribute name never performing filtering. I am guessing it is trying search for e.qty but cannot find it.
So, I tried to filter differently:
$products->getSelect()->where("`qty` > 0");
$products->getSelect()->where("`is_in_stock` = 1");
This is not filtering as well even though, if you look at its sql query, (var_dump((string) $products->getSelect())), and run that query in phpMyAdmin, it works.
Alan Storm in his tutorial mentions that 'The database query will not be made until you attempt to access an item in the Collection'. So, I make the $products->getFirstItem() call but it still not executing the query or filtering in another words.
What am I doing wrong? Any ideas how to filter by attributes that are joined to the table?
Thank you again,
Margots
I would suggest that you try using $products->addAttributeToFilter... instead of $products->addFieldToFilter... - the addField method only works when the field is on the main table that you are querying (in this case catalog_product_entity). Because the inventory fields are in a joined table, you need to use addAttribute.
Hope this helps,
JD
After looking under the hood I learned that _selectAttributes instance field was not assigned in Mage_Eav_Model_Entity_Collection_Abstract class and that is why get exception. A solution usually would be what Jonathan Day suggested above - add addAttributeToFilter() method, however. It will return error since it cannot find such attribute for catalog/product. (qty and in_invetory are in cataloginventory_stock_item). I found two solutions to my problem both required going different direction:
One involved pursuing a way to query the Select statement that I had set for product collection(see above) but somehow it was not resetting the collection with new product. WhenI copied that Sql statment in phpMyAdmin, it worked, so how to query that statement from product collection:
$stmt = $products->getConnection('core_write')->query($products->getSelect()->__toString());
while($rows = $stmt->fetch(PDO::FETCH_ASSOC)){
echo "<br>Product Id ".$rows['entity_id'];
}
Instead of using catalog/product entity table I used the flat table - cataloginventory_stock_item to accomplish the same thing
$stockItem = new Mage_CatalogInventory_Model_Stock_Item();
$stockItems->addQtyFilter('>',0);
$stockItems->addFieldToFilter('is_in_stock',array('eq'=>'1'));
Now there is a collection of all products with qty > 0 and that are in stock.

Resources