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.
Related
I have to create a new record on DirPartyTable .
I used this code:
DirPartyTable _myDirPartyTable;
myPartyNumber = DirPartyTable::getNewPartyNumber((webSession() == null));
myDirPartyTable = DirPartyTable::createNew(myPartyType , myName, myPartyNumber);
// or without partynumber
myDirPartyTable = DirPartyTable::createNew(myPartyType , myName);
But in both cases the standard method generate two Sequence ParyNumber.
In Debu mode I saw the system get two times the PartyNumber .
How can I create a record without creating holes in the sequence numeric?
I tried to use this another code:
select forupdate myDirPartyTable;
myDirPartyTable.name = ....;
//etc
myDirPartyTable.insert();
But does not create the record.
Thanks,
enjoy!
DirPartytable is the basis table of address book framework and is the top level of table inheritance hierarchy see references in Implementing the Global Address Book Framework (White paper)
You shouldn't try to directly create a party but create from your entity which implement the DirParty framework. When you create a vendor, you don't create a DirParty (with herited tables) and then create a VendTable and link them all together. Creating a vendor will do it directly.
Then, if you still encounter two times Party number calls, log a case at Microsoft support.
ulisses: I solved problem, I have to create before the PartyType (Organization or Person etc...)and after update my created DirPartyTable.
Just try the AxDirPartyTable Class like:
AxDirPartyTable axDirPartyTable;
axDirPartyTable = AxDirPartyTable::construct();
axDirPartyTable.parmName(myName);
axDirPartyTable.parmPartyNumber(myPartyNumber);
axDirPartyTable.save();
Examples could be found here: How to create a record in Global address book throgh code.
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 :)
I have 3 tables that provide geolocation data (simplified for this post).
countries:
id:
name:
country_regions:
id:
country_id: (fk to countries:id)
name:
country_region_cities:
id:
country_region_id: (fk to country_regions:id)
postal_code:
name:
When someone goes to add an address I want them to choose a country and enter their postal code. Since multiple cities in the world may have the same zipcode this allows us to figure out where they are. in the country_region_cities table. Using a query similar to this.
SELECT
crc.id as crc_id
FROM
countries c,
country_regions cr,
country_region_cities crc
WHERE
c.id=231 and
crc.postal_code='02199' and
cr.country_id = c.id and
cr.id = crc.country_region_id
The is to store on crc.id in any table that has the need for address data instead of storing full addresses. I am not sure how to add the 2 fields (country_id, and postal_code) to a form and get the country_region_city:id stored in the database field for the table that needs it.
I have a feeling I need to make a AbstractType and link it to the form that needs to store the relationaship, but then do I need to create more AbstractTypes for the other 2 tables involved?
The way to do this is through a custom data transformer object as described in the Symfony cookbook.
http://symfony.com/doc/current/cookbook/form/data_transformers.html
Once I realized this I completed the task in a few hours, and it is happily working!
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.
This is a follow-up to the discussion in this post. I am not sure how to ask the follow-up question in a 2-month-old thread; so I apologize if there is a better way.
Anyway, to reiterate I have an ASP.NET MVC Edit form, and one of the fields is a select list. I get the values fine, but I am having trouble to update the primary entity after Post. Obviously, I have the key to the lookup entity, but it seems crazy to have to load all lookups. So, the suggested solution is entity reference
For clarity, let's say I have a customer as main entity, and title (Mr / Mrs / Dr, etc.) as the lookup.
So, the link above suggests the following:
customer.TitleReference.EntityKey = new EntityKey("MyEntities.Titles", "Id",
Int32.Parse(formData["personTitle"]);
So far, so good. I assign the entity key (and I see in the debugger that is indeed what I expect). But I can't figure out how to get the new value saved along with other customer fields. I am doing the following:
var originalCustomer = (from c in MyEntities.Customers
where c.Id = customer.Id select c).first();
MyEntities.ApplyPropertyChanges(originalCustomer.EntityKey.EntitySetName,
customer);
This updates all customer fields, except for lookups. Intuitively, it is (somewhat) understandable, since if I specify originalCustomer.EntityKey.EntitySetName, ApplyPropertyChanges ignores originalCustomer.TitleReference.EntityKey.EntitySetName.
But if I do specify originalCustomer.TitleReference.EntityKey.EntitySetName, runtime complains that the entity is null (which is also understandable, since I didn't assign anything to the entity; only to entity reference.
As is probably obvious, I am going circles around what seems to be quite straightforward situation. However, I can't find any tutorials that cover it (which is strange in itself).
Furthermore, I have a more complex problem... the customer may have multiple addresses and the address has state... hopefully, once I figure out the titles - I can extrapolate.
By the way, the example (customer - title - address) is fictitious; but it models the problem quite well.
This should work:
var originalCustomer = (from c in MyEntities.Customers
where c.Id = customer.Id select c).First();
originalCustomer.TitleReference.EntityKey = new EntityKey("MyEntities.Titles", "Id",
Int32.Parse(formData["personTitle"]);
context.SaveChanges()