X++ Set Based Operations : Update_recordset
Merhaba,
X++ Set Based Operations : Update_recordset
İyi seyirler.
Archive for the ‘ x++ ’ Category
Merhaba,
X++ Set Based Operations : Update_recordset
İyi seyirler.
Merhaba,
X++ Set Based Operations : RecordSortedList & RecordInsertList
İyi seyirler.
Merhaba,
Sonunda YouTube kanalımı açtım videolarımı çekmeye başladım.
Yazdığın ön bellek makalelerini açıklamaya çalıştığım video aşağıda. Umarım daha güzel videolarla devam edebilirim.
İyi seyirler.
Merhaba
Ax ta E-Fatura süreçleriyle uğraşmayan yoktur. Biz SimpleUBL adında bir eklentiyle hem XML hemde UBL oluşturuyoruz. Şöyle bir ihtiyaç oldu bazı durumlarda bir alanın verisinin değiştirilmesi gerekiyor. Örneğin matbu irsaliye numarası. Bunun için gerekli tablolarda veriyi güncelleyip sonra E-Fatura UBL ini oluşturan kodu tekrar çağırınca direk güncelliyor. Ancak bu işlem biraz yavaş çok fazla kontrol ve işlem yapıyor. Bense çok basit sadece bir alanı değiştirmek istiyorum ve çok hızlı olması gerekiyor. Bunu için str fonksiyonlarından faydalanarak bir metot yazdım. Umarım işinize yarar.
Örnek UBL in bir parçası şöyle:
<?xml version="1.0" encoding="utf-8"?> <cbc:UBLVersionID>2.1</cbc:UBLVersionID> <cbc:CustomizationID>TR1.2</cbc:CustomizationID> <cbc:ProfileID>TEMELFATURA</cbc:ProfileID> <cbc:ID>YTD20182222225</cbc:ID> <cbc:CopyIndicator>false</cbc:CopyIndicator> <cbc:UUID>E01C17F9-B13B-4Df5E-BB5A-92F52E17ff3AB0</cbc:UUID> <cbc:IssueDate>2018-09-01</cbc:IssueDate> <cbc:IssueTime>06:43:11.0000000+03:00</cbc:IssueTime> <cbc:InvoiceTypeCode>ISTISNA</cbc:InvoiceTypeCode> <cbc:Note>Yazıyla : TRY TRY</cbc:Note> <cbc:Note>TOPLAMADET: 1 / 22,00 </cbc:Note> <cbc:LineCountNumeric>1</cbc:LineCountNumeric> <cac:OrderReference> <cbc:ID>YKS022222222</cbc:ID> <cbc:IssueDate>2018-08-08</cbc:IssueDate> <cbc:IssueTime>07:25:31.0000000+03:00</cbc:IssueTime> </cac:OrderReference> <cac:DespatchDocumentReference> <cbc:ID>SVK000343433</cbc:ID> <cbc:IssueDate>2018-09-01</cbc:IssueDate> </cac:DespatchDocumentReference> <cac:Signature> <cbc:ID schemeID="VKN_TCKN">11111111</cbc:ID> <cac:SignatoryParty>
Ben ‘cbc:id SVK000343433 cbc:id’ buradaki SVK000343433 bu numarayı değiştirmek istiyorum.
Bir kaç zorluk var. Birincisi bu numaranın uzunluğu sabit değil.
İkincisi Her bir satır yeni satir karakteriyle ayrılmış durumda.
static void FD_UpdateXml(Args _args) { EInvoiceTable eInvoiceTable; str str1; str str2; str str3; int i ,j; num packingSlipId = "FD000002"; ; ttsBegin; select forUpdate eInvoiceTable where eInvoiceTable.RecId == 5637292931; str3 = strFmt("<cbc:ID>%1</cbc:ID>" , DmrPackipackingSlipIdngSlipId ); // Değiştireceğim metin i = strScan( eInvoiceTable.InvoiceXml , // İrsaliye numrasının başladığı yeri tespit ediyorum. "<cac:DespatchDocumentReference>" , 0, strLen(eInvoiceTable.InvoiceXml)); str1 = subStr( eInvoiceTable.InvoiceXml , i +31 , 50); // Karakteri sayip ilerletiyorum ve 50 karakterlik bir parçayı alıyorum //str1 böyle bir string oluyor <cbc:ID>YKS000017102</cbc:ID> <cbc:IssueDa j = strScan( str1 ,"</cbc:ID>" , 0, strLen(str1)); // Numaranın bitişini tespit ediyorum. str2 = subStr( eInvoiceTable.InvoiceXml , i +31 ,j+8); // Değiştirmek istediğim metne ulaşıyorun // str2 <cbc:ID>YKS000017102</cbc:ID> bu oldu eInvoiceTable.InvoiceXml = strReplace(eInvoiceTable.InvoiceXml ,str2 ,str3); // str2 yi str3 ile değiştiriyorum. eInvoiceTable.update(); ttsCommit; }
Selamlar.
Merhaba
Ax’ta ilk versiyonlardan beri transaction (ttsBegin ttsCommit) blokları dediğimiz bir yapı vardır. Veri tutarlılığı için çok önemli olan roleback dediğimiz bir hata durumunda o blokta yapılan işlemlerin tümünü geri almayı sağlayan bir yapıdır. Ax ta çok sıklıkla kullanılır. Veri tabanında yapılacak işlemlerde mutlaka kullanmak gerekir. Benim bu yazıda bahsedeceğim konu bu bloklar arasında bir hata oluştuğunda roleback in dışında tutmak istediğim bir işlemi nasıl sağlayabileceğim. Genelde bu log atma bildirim gönderme gibi işlemler olur. Bir örnekle açıklamaya çalışayım. Eski sistemden satış verilerinin geldiği ve axta sipariş oluşturulup deftere nakledinlen bir yapı olsun. Herhangi bir sebepten sipariş oluşturulamadığında bir log tablosuna kayıt atmak istiyorum. Örnek kodlar şöyle olsun:
server static void FD_UserConnection1(Args _args) { DmrSalesOrderHeader DmrSalesOrderHeader = DmrSalesOrderHeader::findByRecid(325355554); DmrSalesOrderLine DmrSalesOrderLine; ; ttsBegin; try { // Sipariş başlığı oluşturma kodu while select DmrSalesOrderLine where DmrSalesOrderLine.OrderId == DmrSalesOrderHeader.OrderId { // Sipariş satırı oluşturma kodu throw error("hata"); } } catch { DmrExceptionTable::findOrCreate("Satır oluşturulurken bir hata oluştu.", "DmrCreateSalesOrder", DmrSalesOrderHeader.recid,DmrSalesOrderHeader.tableId,DmrSalesOrderHeader.recversion); } ttsCommit; }
Bu metod sipariş oluşturmaya çalışırken herhangi bir satırda hata veriyor. Bizde hata verdiğini yakalayıp hangi kayıttan sipariş oluştururken hata verdiğini bir tabloya yazmaya çalışıyoruz. Normalde hata verdiği ve tts blokları olduğu için bizim yazmaya çalıştığımız veriyi de geri alacaktı ancak UserConnection ve UnitOfWork kullanarak bunu engelliyoruz. Metot aşağıda.
static server void findOrCreate( str1260 _exceptionDetail, str 100 _className, RefRecId _RefRecId, RefTableId _RefTableId, RefRecId _RefRecVersionId) { DmrExceptionTable DmrExceptionTable; UserConnection UserConnection; UnitOfWork UnitOfWork; ; UserConnection = new UserConnection(); UserConnection.ttsbegin(); UnitOfWork = new UnitOfWork(); DmrExceptionTable.ExceptionDetail = _exceptionDetail; DmrExceptionTable.ClassName = _className; DmrExceptionTable.RefRecId = _RefRecId; DmrExceptionTable.RefTableId = _RefTableId; DmrExceptionTable.RefRecVersionId = _RefRecVersionId; UnitOfWork.insertonSaveChanges(DmrExceptionTable); UnitOfWork.saveChanges(UserConnection); UserConnection.ttscommit(); }
Selamlar.
Merhaba
İş akışında yeni bir atama oluştuğunda atanan kişinin aynı iş akışında daha önce onayı var mı diye kontrol etmek için aşağıdaki metodu kullanabilirsiniz. Bu metodu WorkflowTrackingTable’ a yazdım ama farklı bir yere de yazılabilir. İş akışı altyapısı biraz karışık standart yapıyı değiştirirken dikkatli olmakta fayda var.
Boolean dmrExistApprove() { boolean ret = false; WorkflowTrackingStatusTable workflowTrackingStatus; WorkflowTrackingStatusTable workflowTrackingStatusExist; WorkflowTrackingTable workflowTrackingTable; ; workflowTrackingStatusExist = WorkflowTrackingStatusTable::findRecId(this.WorkflowTrackingStatusTable); select firstOnly RecId, User from workflowTrackingTable exists join workflowTrackingStatus where workflowTrackingTable.WorkflowTrackingStatusTable == workflowTrackingStatus.RecId && workflowTrackingTable.TrackingType == WorkflowTrackingType::Approval && workflowTrackingTable.User == this.User && workflowTrackingTable.RecId != this.RecId && workflowTrackingStatus.ContextRecId == workflowTrackingStatusExist.ContextRecId && workflowTrackingStatus.InstanceNumber == workflowTrackingStatusExist.InstanceNumber && workflowTrackingStatus.ContextTableId == workflowTrackingStatusExist.ContextTableId && workflowTrackingStatus.TrackingStatus != WorkflowTrackingStatus::Cancelled ; if(workflowTrackingTable.recid) { ret = true; } return ret; }
Selamlar.
Merhaba
Ax2012′de iş akışını onaylamak için aşağıdaki örnek kodu kullanabilirsiniz.
static void FD_WorkflowWorkItemTable(Args _args) { WorkflowWorkItemTable WorkflowWorkItemTable; ; WorkflowWorkItemTable = WorkflowWorkItemTable::findRecId(123434343); if(WorkflowWorkItemTable) { ttsBegin; WorkflowWorkItemActionManager::dispatchWorkItemAction( WorkflowWorkItemTable, // Work item record for which the action is being taken strFmt("Auto Approve by %1 ", curUserId() ), // Comment associated with this action curUserId(), //The target user of the action WorkflowWorkItemActionType::Complete,//The work item action type to take "PurchTableApprovalApprove", // The name of the menu item from which the action originated false // Flag denoting if this request originated from web or rich client ); ttsCommit; } }
Selamlar.
Merhaba,
Bir müşterimizde Satınalma talep konsolidasyonu formu çok geç açılıyordu. Öncelikle sorunun ne olduğunu anlamadım. Formun veri kaynağında yavaşlığa sebep olacak bir tablo yoktu. Indexler düzgün bağlantılarda sorun yok. Formun açılışına yazılmış bir kod yok. Bir süre inceledikten sonra Partları gördüm. Partların kodunu inceleyince sorunu hemen tespit ettim.
public void linkActive() { PurchReqConsolidationLine purchReqConsolidationLine; PurchReqLine purchReqLine; purchReqConsolidation = element.args().record(); super(); delete_from purchReqTmpConsolidationPart; while select purchReqConsolidationLine { purchReqLine = PurchReqLine::find(purchReqConsolidationLine.PurchReqLineID); purchReqTmpConsolidationPart.PurchReqConsolidationLine = purchReqConsolidationLine.RecId; purchReqTmpConsolidationPart.PurchReqConsolidationID = purchReqConsolidationLine.PurchReqConsolidationId; purchReqTmpConsolidationPart.AmountBeforeConsolidation = purchReqConsolidationLine.calcAmountBeforeConsolidation(); purchReqTmpConsolidationPart.AmountAfterConsolidation = purchReqConsolidationLine.calcAmountAfterConsolidation(); purchReqTmpConsolidationPart.VendAccount = purchReqConsolidationLine.NewVendor; purchReqTmpConsolidationPart.setDataAreaForModifiedField(fieldNum(PurchReqTmpConsolidationPart, VendAccount), purchReqLine.buyingLegalEntity2DataArea()); purchReqTmpConsolidationPart.CurrencyCode = purchReqLine.CurrencyCode; purchReqTmpConsolidationPart.doInsert(); } this.executeQuery(); this.research(); }
Part formlarından biri olan PurchReqConsolidationPartByVendor formunun LinkActive metoduna yazılan koddaki temel hatayı hemen anlamışsınızdır. join yazılması gereken yerde find kullanılmış. Partları aktif olarak kullanmadığı için bu formdan kaldırıp formun çok daha hızlı açılmasını sağladım.
Daha vahim bir durum Satınalma talep konsolidasyonu formundan bir buton ile açılan PurchReqConsolidationAddLine formunda vardı.
PurchReqConsolidationAddLine Formunun PurchReqLine veri kaynağının init metodunda aşağıdaki kod mevcut.
public void init() { PurchReqLine tmpPurchReqLine; PurchReqTable tmpPurchReqTable; QueryBuildRange qbr; super(); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionPurpose)); qbr.value(SysQuery::value(RequisitionPurpose::Consumption)); qbr.status(RangeStatus::Locked); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,PurchLineCreated)); qbr.value(SysQuery::value(NoYes::No)); qbr.status(RangeStatus::Locked); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionStatus)); qbr.value(queryValue(PurchReqRequisitionStatus::Approved)); qbr.status(RangeStatus::Locked); purchReqLine.setTmp(); while select tmpPurchReqLine join RecId, SourceDocumentHeader from tmpPurchReqTable where tmpPurchReqLine.PurchReqTable == tmpPurchReqTable.RecId && tmpPurchReqLine.IsPreEncumbranceRequired != UnknownNoYes::Unknown { if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) && !PurchReqConsolidationLine::findByReqLineId(tmpPurchReqLine.RecId)) { purchReqLine.data(tmpPurchReqLine.data()); // Since tempory table will overwrite the RecId value, so assign RecId into an unused field purchReqLine.AddressRefRecId = tmpPurchReqLine.RecId; purchReqLine.doInsert(); } } }
PurchReqConsolidationLine join yerine if ile kontrol edildiği için bu formun açılması da dakikalar sürüyordu. Kodu aşağıdaki hale çevirince saniyeler içinde açılmaya başladı.
public void init() { PurchReqLine tmpPurchReqLine; PurchReqTable tmpPurchReqTable; purchReqConsolidationLine purchReqConsolidationLine; QueryBuildRange qbr; super(); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionPurpose)); qbr.value(SysQuery::value(RequisitionPurpose::Consumption)); qbr.status(RangeStatus::Locked); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,PurchLineCreated)); qbr.value(SysQuery::value(NoYes::No)); qbr.status(RangeStatus::Locked); qbr = purchReqLine_ds.query().dataSourceTable(tableNum(PurchReqLine)).addRange(fieldNum(PurchReqLine,RequisitionStatus)); qbr.value(queryValue(PurchReqRequisitionStatus::Approved)); qbr.status(RangeStatus::Locked); purchReqLine.setTmp(); while select tmpPurchReqLine join RecId, SourceDocumentHeader from tmpPurchReqTable where tmpPurchReqLine.PurchReqTable == tmpPurchReqTable.RecId && tmpPurchReqLine.IsPreEncumbranceRequired != UnknownNoYes::Unknown // Dmr FD --> notExists join purchReqConsolidationLine where tmpPurchReqLine.RecId == purchReqConsolidationLine.PurchReqLineID // Dmr FD <-- { // Dmr FD --> //if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) && // !PurchReqConsolidationLine::findByReqLineId(tmpPurchReqLine.RecId)) if (PurchReqPurchaseOrderGenerationRule::canConsolidateStatic(tmpPurchReqLine) ) // Dmr FD <-- { purchReqLine.data(tmpPurchReqLine.data()); // Since tempory table will overwrite the RecId value, so assign RecId into an unused field purchReqLine.AddressRefRecId = tmpPurchReqLine.RecId; purchReqLine.doInsert(); } } }
İki kod arasındaki farkı şöyle özetleyebilirim. İlk hali bütün PurchReqLine ları dolanıp PurchReqConsolidationLine da olmayanlar için temp kayıt oluşturur.
İkinci hali ise NotExists join ile bağlandığı için zaten sadece PurchReqConsolidationLine olmayan PurchReqLine ları getirecektir. Dolayısıyla çok daha hızlı olacaktır.
Performans ERP yazılımında belkide en çok dikkat edilmesi gereken konulardan biri. Yazdığımız her kodun birilerinin günlük hayatı olduğunu anlayıp mümkün olduğunda performanslı olmasına dikkat etmek gerekiyor. Gördüğünüz gibi çok basit dokunuşlarla çok büyük kazanımlar elde edebiliyorsunuz. Burada standart kodda böyle bir hatayla karşılaşmak beni şaşırttı. Genelde Standart kod çok kalitelidir ama bazen böyle gözden kaçan kodlar da olabiliyor.
Selamlar.
Merhaba
Muhasebe günlük satırlarından (LedgerJournalTrans) bir alanı Tedarikçi hareketlerine (VendTrans) ve Tedarikçi açık hareketlerine (VendTransOpen) taşımak için aşağıdaki adımları takip edebilirsiniz.
Bu adımlarla DmrWfId alanını istediğimiz tablolara taşımış olduk.
Selamlar.
Merhaba,
Ax gerçekten bir çok ayrıntıya sahip. Özellikle nesnelerin özelliklerinde aktif olarak kullanmadığımız bir çok yenilik mevcut. AutoRefreshData özelliği de bunlardan biri.
Yaptığım bir geliştirmede ListePage formunda durum değiştiren bir sınıf yaptım. Bildiğiniz gibi ListPage formlara kod yazamıyoruz. Bu sınıf ListePage’teki satırın durum alanını değiştiriyor. Bu değişikliği yaptıktan sonra formu yenilemem gerekiyordu. Bunu yapmak için args üzerinde DateSource alıp üzerinden Refresh çalıştırmak gerekiyor ama güzel bir yöntem değil. Bunun daha kolay bir çözümü olmalı derken buton üzerindeki AutoRefreshData özelliğini gördüm. Bu özelliği true olarak ayarlağınızda aslında Table_DS.Research(True) metodunun yaptığı işi yapıyor. Bu sayede kod yazmadan griddeki veriyi güncelleniyor.
Selamlar.
Pts | Sal | Çar | Per | Cum | Cts | Paz |
---|---|---|---|---|---|---|
« Eyl | ||||||
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |