Posts Tagged ‘ x++

X++ :13- Update Metodu Ne İşe Yarar?

Bu yazıda Dynamics 365 Finance and Operations tablo metotlarından Update() metodunu anlatacağım. Önceki yazılarımda tabloların temel yapılarından bahsetmiştim. Şimdi ayrıntılı olarak alt seviye nesne ve metotları inceleyeceğim. Tablolar XRecord sınıfından türetilen aslında SQL tabloların sınıf karşılıklarıdır. Sınıflardan temel bir iki farkı vardır. Sınıf olmasına rağmen New ile nesne oluşturmak gerekmez. Alanlar metot gibi davranın ama paranteze ihtiyaç duymaz. Update() metodu tabloda bir kayıt güncellenirken çalışan metottur.

FDBookTable tablosunda Metotlara sağ tıklayıp Update() metodunu Override ettim.

Resim-1

Super()’i daha önce anlatmıştım. Aslı işi yapan yanı DB’deki veriyi güncelleyen ana sınıftaki kodu çağırır. Basit bir kod yazdım her seferinde sayıyı bir arttıracak şekilde.

Resim-2

Tablo tarayıcısını açıp herhangi bir güncelleme yaptığımda sayının arttığını gördüm.

Resim-3

void update(boolean _updateSmmBusRelTable = true, boolean _updateParty = true)

{

CustTable   this_Orig = this.orig();

RecVersion  rv = this_Orig.RecVersion;

ttsbegin;

super();

// Update the full text search table

MCRFullTextSearch::update(this);

this.SysExtensionSerializerMap::postUpdate();

if (_updateSmmBusRelTable)

{

smmBusRelTable::updateFromCustTableSFA2(this, ”, false);

}

if (this_Orig.CustGroup != this.CustGroup)

{

ForecastSales::setCustGroupId(this.AccountNum,

this_Orig.CustGroup,

this.CustGroup);

}

smmTransLog::initTrans(this, smmLogAction::update);

// If the customer group has changed

if (this.CustGroup != this_Orig.CustGroup)

{

// clear the ledger cache

LedgerCache::clearScope(LedgerCacheScope::PartyMainAccountDimensionListProvCust);

}

if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoMX]) && _updateParty)

{

this.copyInfoToParty();

}

ttscommit;

}

CustTable Update() metodunu inceleyelim. Güncelleme olduktan sonra eğer belli bir alanın güncellenip güncellenmediğini kontrol etmek istiyorsanız this.Orig() ile kaydın orijinal halini alıp kıyaslayabilirsiniz.

CustTable   this_Orig = this.orig();

if (this.CustGroup != this_Orig.CustGroup)

Bu iki satır kullanıma güzel bir örnek.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Update, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :12- Insert Metodu Ne İşe Yarar?

Bu yazıda Dynamics 365 Finance and Operations tablo metotlarından Insert() metodunu anlatacağım. Önceki yazılarımda tabloların temel yapılarından bahsetmiştim. Şimdi ayrıntılı olarak alt seviye nesne ve metotları inceleyeceğim. Tablolar XRecord sınıfından türetilen aslında SQL tabloların sınıf karşılıklarıdır. Sınıflardan temel bir iki farkı vardır. Sınıf olmasına rağmen New ile nesne oluşturmak gerekmez. Alanlar metot gibi davranın ama paranteze ihtiyaç duymaz. Insert() metodu tabloda bir kayıt oluşturan metottur.

FDBookTable tablosunda Metotlara sağ tıklayıp Insert() metodunu Override ettim.

Resim-1

Insert işlemini yani kaydın DB’ye yazılmasını aslında Super() yapar. Super() ezilen metodu çağıran koddur. Dolayısıyla Super()’den önce kayıt henüz DB ye yazılmamıştır. Kaydetmeden önce bir işlem yapmak istiyorsanız Super()’den önce sonra yapmak istiyorsanız Super()’den sonra yazmalısınız. Ben Basit bir satır ekledim. Çok doğru bir yer olmamakla birlikte çalışır. Insert() metodu validasyon için kullanılmamalıdır.

Resim-2

Tablo tarayıcısını açıp yeni bir kayıt oluşturduğumda otomatik BookCount 1 olarak oluşuyor.

Resim-3

Şimdi standart bir tablonun Insert() metoduna bakalım. Kodları görebilmek için View code demeniz gerekiyor.

Resim-4

void insert(DirPartyType _partyType = DirPartyType::None, Name _name = ”,boolean _updateCRM=true)

{

DirPartyType   type;

ttsbegin;

// Check if not associated to Party

if (!this.Party)

{

// Create a Party entry for customer

this.Party = DirPartyTable::createNew(_partyType, _name).RecId;

}

else

{

if (!this.VATNum)

{

this.VATNum = TaxRegistration::getPrimaryRegistrationNumber(DirPartyTable::findRec(this.Party), TaxRegistrationTypesList::TAXID);

}

this.initFromsmmLeadTable();

}

smmBusRelTable smmBusRelTable = smmBusRelTable::findByParty(this.Party, true);

if (smmBusRelTable.RecId)

{

smmBusRelTable.BusRelTypeId = smmBusRelTypeGroup::getCustomerType();

smmBusRelTable.update(false);

}

super();

// Insert new customer in full text search table

MCRFullTextSearch::insert(this);

this.SysExtensionSerializerMap::postInsert();

if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoMX]))

{

this.copyInfoToParty();

}

smmTransLog::initTrans(this, smmLogAction::insert);

DirPartyRelationship::createLegalEntityRelationship(this.Party, this.DataAreaId, DirSystemRelationshipType::Customer);

// Add links to contact person

ContactPerson::addCustVendLink(this.TableId, this.Party, this.AccountNum);

// Create default location if using existing party

LogisticsLocationDefaultAppUtil::createDefaultForExistingParty(this);

DimensionDefaultFacade::copyDimensionValueToDefaultDimensionField(this, fieldNum(CustTable, AccountNum), this, fieldNum(CustTable, DefaultDimension));

ttscommit;

}

Bu kodda iki yere dikkat çekeceğim. Birincisi if (!this.Party) ile başlayan kod burada eğer parti kodu yoksa oluşturmak üzerine bir kod var. Superden önce çünkü kayıt oluşmadan önce oluşmalı ve CustTable’daki ilişkili alana yazılmalı. İkincisi ContactPerson::addCustVendLink(this.TableId, this.Party, this.AccountNum); ilgili kişi oluşturma. Burada da müşteri kaydı oluşmalı ki müşteri kodu kullanılarak ilişkili kişi kaydı oluşturulsun.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Insert, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :11- Security Privileges ve Duties Nedir?

Bu yazıda Dynamics 365 Finance and Operations güvenlik için temel yapılar olan Privilage ve Duty den bahsedeceğim. Güvenlik çok geniş bir konu ama bu yazdı bir yazılımcının proje yaparken oluşturması gereken temel güvenlik nesnelerini anlatacağım. Bu seride kullandığımız MenuItem için güvenlik nesneleri oluşturalım.

Resim-1

Öncelikle ayrıcalık (Privilage) oluşturalım.

Resim-2

Güvenlik Entry Point üzerinden verilir. FDBookTable MenuItem’mini sürükleyip bir giriş noktası oluşturalım. Access Level kısmında tam yetki vermek için Delete seçebilirsiniz. Yukarı çıktıkça yetki seviyesi azalır.

Resim-3

Şimdi görev Duty oluşturalım.

Resim-4

Oluşturduğum göreve ayrıcalığı ekleyelim. Artık BookManagment projemiz için oluşturduğumuz tüm ayrıcalıkları bu göreve ekleyebiliriz. Bu aşamadan sonra istenilen role yetki vermek için bu görevi kullanabiliriz. Yetkileri ara yüzden veren arkadaşlara kolaylık olması için etiketlerin doğru ve yeterli ayrıntıda olması faydalı olur.

Resim-5

Bu yazıda geliştirme yaparken gerekli olan temel güvenlik nesnelerini anlatmaya çalıştım. Güvenlik genelde en sona bırakılan bir konu oluyor ama mutlaka testlerin Admin yetkisiyle değil gerekli güvenlik rolleriyle test edilmesi gerekiyor. Canlı geçişlerde bu konuyla ilgili çok sorunla karşılaştım.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Privilage,Duty, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :10- Query Nedir?

Bu yazıda X++’ın en güçlü özelliklerinden biri olan Query’leri inceleyeceğim. Query nesne tabanlı sorgu olarak özetleyebiliriz. Bu sorgu nesne bazlı olduğu için istenildiği gibi değiştirilebilir ayrıca son kullanıcı ile iletişime sokulabilir. Bu sebeplerden Form ve raporların temel veri kaynağı Query’dir.  Hem kodla hem de ara yüzden oluşturabiliriz. Form ve view gibi yapılarda kullanmak için genelde ara yüzden oluşturulanlar kullanılır.  Bu yazıda ara yüzden örnek bir Query oluşturacağım. İlerleyen yazılarımda kodla nasıl oluştururuz ve nasıl kullanabiliriz anlatacağım.

Resim-1

Yeni bir Query oluşturuyorum.

Resim-2

Query’de temel birkaç özellik var tabi öncelikle veri kaynağı belirlemelisiniz. Ben veri kaynağı olarak FDBookTable ekledim ve FieldList ile BookId, BookName oluşturdum. Range olarak ta BookCoverTyoe ekledim. Eğer kabaca sorgu olarak yazsaydım şöyle olurdu. Bu sorguyu artık istediğim yerde kullanabilirim.

Select BookI, BookName From FDBookTable

Where FDBookTable.BookCoverTyoe == FDBookCoverType::paperback;

Resim-3

Standartta olan karmaşık bir sorgu örneği. Burada 3 tablo bağlanmış. Filtre ve sıralama verilmiş.

Resim-4

Kullanıcı ile iletişime giren Query ekranı bu şekildedir. Bu resim eski versiyondan ama mantıkta bir fark yoktur. Yukarıdaki sorgunun görsel halini görebildiğimiz gibi buradan kullanıcı istediği işlemleri yapabilir.

Resim-5

Eklediğimiz veri kaynağı otomatik alanları seçilmeden gelecektir. Eğer tüm alanlar seçilsin isterseniz Dynamics Fields özelliğini Yes yapmak gerekir.

Resim-6

Bu yazıda ara yüzden nasıl Query oluşturulur anlatmaya çalıştım. Query çok geniş ve çok kullanılan bir özelliktir. Ayrıntılarına vakıf olmak için bol bol örnek yapmak lazım. Özellikle form tarafında kullanımı konusunda çok zafiyet görüyorum bu konuda ayrı bir yazı hazırlamayı düşünüyorum.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Query, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :9- MenuItems ve Menus Nedir?

Bu yazıda Dynamics 365 Finance and Operations ara yüzlerinin temel nesneleri olan MenuItem ve Menu’lerden bahsedeceğim. MenuItem bir nesnenin giriş noktası olan ve üzerinden yetki tanımı yapılabilen yapılardır. Menu ise adı üzerinde ekrandaki kısa yollardır. Üç tip MenuItem vardır. Display formlar için Output raporlar için Action ise sınıflar için kullanılır.

Resim-1

Önceki yazıda oluşturduğum FDBookTable formu için Display tipinde bir MenuItem oluşturalım.

Resim-2

Mutlaka Label tanımlamalısınız.  ObjectType olarak Form seçiyorum. Object olarak formumu seçiyorum.

Resim-3

Bu MenuItem artık istersek formda buton olarak veya menüye ekleyerek kullanabiliriz. Şimdi biz menüye ekleyelim. Kitap projesini Stok yönetimi altında takip etmek istediğim için Create Extension ile Extension oluşturdum.

Resim-4

Oluşan Extension’a bir alt menü ekliyorum.

Resim-5

Oluşturduğum alt menüye Book Managment etiketini tanımladım. Oluşturduğum menüItem’ı sürükleyip yeni oluşturduğum alt menüye bırakıyorum.

Resim-6

Derleyip projemi çalıştırdığımda uygulamada yeni oluşturduğum menü ve eklediğim MenuItem’ı görebildim.

Resim-7

Bu yazıda ara yüz oluşturmak için kullandığımız Menu ve MenuItem nedir nasıl oluşturulur anlatmaya çalıştım.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Menu,MenuItem, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :8- Form Nedir?

Bu yazıda Dynamics 365 Finance and Operations formlarının temellerinden ve basit bir formu nasıl oluşturabiliriz anlatacağım. Form uygulamanın kullanıcıya sunduğu ara yüzdür. Çok gelişmiş özellikleri olan bu yapının benim en çok sevdiğim tarafı tasarımla sizi uğraştırmamasıdır. Ağaç yapısında alanları eklediğinizde otomatik tasarım ortaya çıkar ve siz tasarımla uğraşmazsınız.

Bir örnek ile açıklamaya çalışacağım.

Resim-1

Önceki yazılarımda oluşturduğum FDBookTable için bir tane form yapalım.

Resim-2

Formlardaki temel nesneler aslında 3 tanedir. Öncelikle Data Source formun temel veri kaynağını gösterir. Burada Table, View veya Query kullanabiliriz. Methods kod yazabildiğimiz yerler sadece ana alanda değil her bir nesneye de kod yazabiliyoruz. Form metodları ile ilgili ayrı bir yazı düşünüyorum. Gelelim Design kısmına burası bizim tasarımı yaptığımız yer. Burada Pattern uygulayabiliriz.Uyguladığınızda temel nesneleri oluşturmanızı bekliyor bu sayede standartta uygun tasarım yapmış olursunuz.

Resim-3

Ben Apply pattern -> Simple List seçtim. Listede birçok Pattern var.

Resim-4

Simple List için gerekli olan nesneler kırmızı ile gösteriliyor. Her nesneyi eklediğinizde burası düzelecek.

Resim-5

Bazı nesneler kendi tasarımına ihtiyaç duyabilir. Şimdi 3 tane gerekli olan nesneyi ekliyorum.

Resim-6

Action Pane Filter grup ve Grid ekledim. İstenenler bunlardı. Burada veriyi gridde göstereceğiz.

Resim-7

Bir nesnenin doğru veri kaynağından veri gösterebilmesi için Data Source’un seçilmesi gerekiyor.

Resim-8

Veri kaynağından istediğim anaları gride bıraktım ve kaydettim. Artık formum hazır sayılır.

Resim-9

Derleyip formumu başlangıç nesnesi olarak seçip projemi çalıştırdığımda formum açılacak. Veri girişi veya diğer  işlemleri yapabilirim.

Resim-10

Bu yazıda Form nedir nasıl oluşturulur anlatmaya çalıştım. Tabi Form çok geniş bir konu çok farklı tipte formlar yapılabiliyor. En kolay öğrenme yöntemi standartta olan formları incelemek. Ayrıca farklı formlar yapıp kendinizi geliştirebilirsiniz. Formlar için en önemli konu veri kaynağı mantığını anlamak. Formlarda aslında Query kullanılıyor eğer Query mantığında eksikleriniz varsa öncelikle onları halledin.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Form, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :7- Views Nedir?

Bu yazıda Dynamics 365 Finance and Operations View’lerinden bahsedeceğim. View aslında SQL View’leriyle aynı. Burada oluşturduğunuz View direk SQL de oluşuyor. SQL de birçok özellikle var hepsi X++ ne yazık ki yok. Ama birçoğunu yapabiliyoruz. Bir tane örnek View yapalım.

Resim-1

Yeni bir nesne oluşturma ekranından View seçiyoruz.

Resim-2

Oluşan View’e veri Data Source olarak FDBookTable ekledim. DataSource Fields kısmında tümünü seç demelisiniz. Sonra Fields kısmına View’de olmasını isteyen alanları ekledim. Tabi bu bir sorgu çok karmaşık sorgular yapmak isterseniz Query kullanmalısınız. Derleme ve eşitlemeden sonra hazır olacak.

Resim-3

Açıp alanlara bakabiliriz.

Resim-4

Tablo gibi kullanabilirsiniz. Tabi View sadece veri okumak için kullanılır. Insert Update Delete için kullanamazsınız.  

Resim-5

Bu yazıda View nedir, nasıl oluşturulur ve nerelerde kullanılır anlatmaya çalıştım. View çok önemli ve hayat kurtaran bir özellik temellerini iyi kavrayıp oluşturduğunuz View’leri SQL den kontrol edip kodlarını anlamaya çalışırsanız çok şey öğrenirsiniz.

Selamlar.

www.fatihdemirci.net

TAGs: X++,View, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :6- Tables Nedir?

Bu yazıda Dynamics 365 Finance and Operations tablolarından bahsedeceğim. Tablo sistemin en önemli nesnesi desek yanlış olmaz. Öncelikle Tablolar gerçek SQL tabloların XRecord sınıfından türeyen sınıf karşılıklarıdır.

Tabloların birçok özelliği var sırayla hepsinden bahsetmeye çalışacağım. Standartta 12000’in üzerinde tablo var.

Resim-1

Yeni bir tablo oluşturalım. FDBookTable bizim temel tablomuz olacak.

Resim-2

Fields alanına önceki yazılarımızda oluşturduğumuz EDT ve Enum’ları kullanarak alanları oluşturdum. Bunu sürükleyip bırakıp yapabilirsiniz.

Fields: Tablonun biz tarafından yönetilen alanlarıdır. SQL’de otomatik alanlar oluşur.

Field groups: SQL de olmayan bir özelliktir. Aynı tipte olan alanları bir araya getirip grup oluşturabilir ve bunları formlarda kullanabilirsiniz. Sonrasında eğer yeni bir alan ekler ve bunu da bir gruba dâhil ederseniz kullanılan tüm formlara otomatik eklenir.

Indexes: Aslında SQL indeksleridir. Burada oluşturulan indeksler otomatik SQL de oluşturulur. Performans için en önemli özelliklerden biri budur. Tabloya göre uygun indeksler belirlenmelidir.

Full Text Indexes: Çok uzun metinlerin içiren kelime aramaları hızlandırmak için oluşturulan indekslerdir. SQL de bire bir oluşur.

Relation: Tabloların birbiriyle olan ilişkilerini tutar. SQL’e yazmaz.

Delete Actions: Bu tablodan kayıt silinmeye çalışıldığında ilişkili tablolarda kayıt varsa nasıl davranması gerektiğine karar verir. X++ ta olan bir özelliktir.

State Machines: İş akışı durumları için kullanılır.

Mappings: Tablolar mapları buradan görüntülenebilir.

Methods: Bir sınıf olduğu için birçok metodu vardır ve bizde ekleyebiliriz.

Events: Tablo üzerinde olan olayları gösterir. Biz X++ yazılımcıları metot kullanmaya daha alışkınız ama yeni versiyonla bunlar biraz değişti.

En temel tablo özelliklerini işaretledim. İleriki yazılarımda tek tek anlatacağım

Resim-3

Modified By ve Modified Date Time özelliklerini açıyorum. Bu sayede otomatik olarak kaydı kim hangi zamanda açmış tutabileceğiz.

Resim-4

Bir tane index oluşturuyorum. En temel anahtar BookId olduğu için onu ekledim. Bu indexi özelliklerden birincil anahtar olarak seçiyorum.

Resim-5

Alanlarında birçok özelliği mevcut. Yine ne temel özelikleri işaretledim.

Resim-6

Tablomu kaydettikten sonra derleme ve eşitleme yapıyorum artık Table Browser ile kontrol edebiliriz.

Resim-7

Bir tane örnek kayıt girdim. Bizim açmadığımız sistemin otomatik açtığı alanlar var onlardan bahsedeyim.

DataAreaId: Şirket kodu eğer tablonuz şirketler üstü değilse bu alan otomatik hangi şirkette işlem yaparsanız o şirket kodu ile dolar.

RecVersion: Kısaca aynı anda iki kullanıcının aynı kaydı güncellemesini engellemek için tutulan rasgele bir sayıdır. Her güncellemede değişir.

Partition: Şirketler üstü bir gruplamanız varsa örneğin finans şirketleri veya üretim şirketleri gibi bunları ayırmak için kullanılır. Şu anda çok aktif kullanılmıyor.

RecId: Tablo bazında tekil int64 tipinde bir anahtardır.

Resim-8

Bu yazıda tabloları size tanıtmaya çalıştım. Bu yazıda ancak kapıyı araladım diyebilirim. Çok fazla ayrıntı mevcut farklı yazılarla tabloları anlatmaya devam edeceğim.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Tables, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :5- Maps Nedir?

Bu yazıda Maps’lerden bahsedeceğim. Map ve Maps karıştırılır genelde ama tamamen farklı nesnelerdir. Maps temelde birbiriyle çok ortak yönü olan tablolar için ortak kod yazma imkânı sunan bir yapıdır. Çok uzun zamandır X++ ile geliştirme yapıyorum ama şimdiye kadar sıfırdan Maps oluşturmadım ama olan ve kullanılan Maps’lere yeni alanlar eklemem gerekti.

Resim-1

En Çok kullanılan Maps’ler genelde CustVend’lerdir. Mappings ile hangi tabloların bağlanacağı seçilir. Filed kısmında ortak alanlar belirlenir. Bu işlemler tamamlandıktan sonra artık buraya kod yazılabilir. Yazılan kod hem Cust hem de Vend için ortak kullanılır.

Resim-2

Data Model->Map ile yeni Map oluşturabilirsiniz.

Resim-3

Bu yazıda Maps’lerden bahsettim. Maps’leri hep nesne tabanlı yazılım geliştirmede işi biraz abartmak olarak gördüm. İşi basitleştirme ve kod çoğaltmamak için yapılan bir yapı ama birçok durumda işi daha zorlaştırıyor.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Container, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

X++ :4- Map Nedir?

Map bir veri yapısı aslında Set’lere benzer bir yapısı var. Bir anahtar ve bir değerden oluşuyor. Uygulamada çok fazla kullanılıyor. Bir örnekle açıklığa kavuşturalım.

Bir Job oluşturuyorum.

Resim-1

Yeni bir değişken oluşturup key ve value tiplerini belirlemek lazım.

class FDMap

{

public static void main(Args _args)

{

Map             map = new Map(Types::Integer, Types::String);

MapEnumerator   mapEnumerator;

map.insert(1, “M001″);

map.insert(2, “M002″);

map.insert(3, “M003″);

map.insert(4, “M004″);

mapEnumerator = map.getEnumerator();

while (mapEnumerator.moveNext())

{

info(strFmt(“Key: %1, Value: %2″,mapEnumerator.currentKey(), mapEnumerator.currentValue()));

}

info(strFmt(“Key: 3, Value: %1″,map.exists(3) ? map.lookup(3) : “not found” ));

map.insert(3, “M023″);

info(strFmt(“Key: 3, Value: %1″,map.exists(3) ? map.lookup(3) : “not found” ));

}

}

Job çıktısı böyle olacaktır.

Resim-2

Bu yazıda Map sınıfının kullanımıyla ilgili bir örnek yaptım. Özellikle küçük ve boyutu belli olmayan veriler için kullanılır. Çok büyük verileri tutmak için uygun değildir.

Selamlar.

www.fatihdemirci.net

TAGs: X++,Container, Azure, Azure DevOps, Microsoft Dynamics 365, MsDyn365FO, MsDyn365CE, MsDyn365

Page 4 of 13« First...2345610...Last »