【DynamicsAX】X++のメモ : ExcelへExport
X++のメモ。
特定のテーブルからExcelにレコードをエクスポート。jobで実行。参考にさせていただいたサイトはaxaptajob.blogspot.comさん。Thank you so much!
static void ExportToExcel(Args _args) { SalesTable SalesTableLocal; // 今回はSalesTableテーブル SysExcelApplication excelApp; SysExcelWorkSheet excelWorksheet; SysExcelRange excelRange; SysExcelCells excelCells; SysExcelCell excelCell; ComVariant cellValue; Int i=1; ; excelApp = SysExcelApplication::construct(); cellValue = new ComVariant() ; excelApp.workbooks().add(); excelWorksheet = excelApp.worksheets().itemFromNum( 1 ); excelCells = excelWorksheet.cells(); while select * from SalesTableLocal { excelCells.item(i,1).value(cellValue.bStr(SalesTableLocal.SalesId)); excelCells.item(i,2).value(cellValue.bStr(SalesTableLocal.SalesName)); // if field's attribute is 'date', we must use 'cellValue.date()'. excelCells.item(i,3).value(cellValue.date(SalesTableLocal.DeliveryDate)); i++; } excelApp.visible(true); //excelApp.quit(); }
新規jobに上記コードを書いて実行すると、SalesTableのSalesId、SalesName、DeliveryDateがずらっと書かれたExcelシートが表示されます。次回はImportっすね。
【DynamicsAX】X++のメモ
X++のメモ。
その1。BOMテーブルをSELECTするだけ。Jobで新規jobを登録、以下のコードを入力、実行。
static void Job1(Args _args) { BOM bomt; // BOMテーブルクラス int rec; // レコード数 ; while select bomt { // BOMテーブルをSELECT info("-- record info --"); info(strfmt("BOMType : %1", bomt.BOMType)); // BOMTypeを表示 info(strfmt("ItemID : %1", bomt.ItemId)); // ItemIdを表示 rec++; // レコード数をインクリメント } info("--end--"); info(strfmt("%1 records was selected", rec)); }
その2。テーブルをjoinした例。BOMTableテーブルとBOMVersionテーブル。
static void Job2(Args _args) { BOMVersion bomv; // BOMVersionテーブル BOMTable bomt; // BOMTableテーブル ; while select bomv // BOMVersionをSELECT join bomt // BOMTableとJOIN // JOINの条件 where bomv.BOMId == bomt.BOMId { // BOMIdとSiteIdを表示 info(strfmt("ItemID, BOMId, SiteId : %1, %2, %3", bomv.ItemId, bomv.BOMId, bomt.SiteId)); } }
その3。サーバーの指定したフォルダに、テーブルから読み出したレコードをファイル出力するだけ。
static server void Job3(Args _args) { TextIo file; FileName fileName = @"c:\tmp\test.txt"; BOMVersion bomv; container con; FileIoPermission permission; #File ; try { permission = new FileIoPermission(fileName, #io_write); permission.assert(); file = new TextIo(fileName, #io_write); if (!file) { throw Exception::Error; } file.outRecordDelimiter(#delimiterCRLF); file.outFieldDelimiter(";"); while select bomv { con = connull(); con = conins(con, 1, bomv.ItemId); con = conins(con, 2, bomv.BOMId); file.writeExp(con); } } catch (Exception::Error) { error("you can't access to file.");} CodeAccessPermission::revertAssert(); }
参考資料:Microsoft Dynamics AX 2009 Programming: Getting Started
【iOS】Core Data & TableViewで、確認画面表示
前回のバグはちょっと置いておいて。TableViewで表示されているレコードをタップして、詳細(?)画面を表示させるように出来た。今回参考にさせていただいたのは、独学者の独り言(原宏之さん)の記事。ありがとうございます。
まずはスクリーンショットから。Steveさんが2件登録されているのはご愛嬌前回の名残。
次にコード。元のViewから今回の詳細画面へ移動する際に使用するメソッドを、以下のように書くとOK。
// レコードを更新したいとき、ここを使おう // レコードをタップすると、ビューが移動する - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 詳細ページ用に、DetailViewというファイルを用意(.h、.m、.xibを作成しておいた) DetailView *detailViewController = [[DetailView alloc]initWithNibName:@"DetailView" bundle:nil]; // 元のページから、pushで移動 [self.navigationController pushViewController:detailViewController animated:YES]; // 最初の画面からindexPath(何行目のデータかってこと)を利用してNSManagedObjectを引き渡す NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; // nameをキーにしてgetした値を表示 detailViewController.nameText.text = [[managedObject valueForKey:@"name"] description]; // 同じくphoneNumberをキーにしてgetした値を表示 detailViewController.phoneText.text = [[managedObject valueForKey:@"phoneNumber"] description]; // 終わったらrelease [detailViewController release]; }
ここまで来たら、あとは更新っすね。
ここまで書いて、ふと思い直してというか、理解が足りないと感じた点を。
上記コードは、元画面の実装ファイル(RootViewController.m)に書いた。つまり、「移動先画面でデータを取得」ではなく、「移動元でデータを取得して、取得したデータを移動先に渡す」ということだと思うのだけど、それがピンときていなかった。
【iOS】Core Dataでfetchされたデータの絞り込み検索(バグ?発見)
これじゃまだまだっす...
iPhone Simulatorで以前登録したデータが、起動時には表示されず、検索で文字を入力すると、ひっかかる
なもんで、同じ名前の場合には警告を出すとか禁止するってのが必要
あと、以前登録したレコードを最初に表示させるってのも必要かな
【iOS】Core Dataでfetchされたデータの絞り込み検索(なんとか解決)
なんとも単純な箇所がネックだった。
前回、Core Dataに登録したデータをUISearchBarで絞り込みしようと思い、以下のメソッドを追加した。
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
上記コードは、Cocoaの日々さんの記事、およびiOS Developer Libraryのサンプルコードを参考にしました。ありがとうございます。
んで
前回のクラッシュは何が原因だったかというと、キャッシュ名でした。元々、
- (NSFetchedResultsController *)fetchedResultsController
というメソッドの中で
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
という箇所があります。ここで、cacheName:@"Root"でRootと指定しています。しかし、
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope { NSString *query = self.searchDisplayController.searchBar.text; if (query && query.length) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; [NSFetchedResultsController deleteCacheWithName:@"Root"]; } NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { // Handle error NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); // Fail } }
の中で、deleteCacheWithName:@"Root"というとこ、ここを"UserName"と書いていたんす。これを合わせたら、OKでした。スクリーンショットを以下に。
と、いうわけで、次は、登録したレコードの更新を。
【iOS】Core Dataでfetchされたデータの絞り込み検索(未解決)
predicateWithFormatが、おそらくSQLでいうところのwhere句に相当するのでは?というところまではあたりはつけたが...
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope { NSString *query = self.searchDisplayController.searchBar.text; if (query && query.length) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@", query]; [self.fetchedResultsController.fetchRequest setPredicate:predicate]; [NSFetchedResultsController deleteCacheWithName:@"UserSearch"]; } NSError *error = nil; if (![self.fetchedResultsController performFetch:&error]) { // Handle error NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); // Fail } }
上記コードでビルドしてシミュレートした時。Sを入力した段階では、2件あるレコードともに同じなのだけど、このあとtを入力したら、あっというまにクラッシュ。
キャッシュが悪さをする的な記事がwebにあったのだけど、
[NSFetchedResultsController deleteCacheWithName:@"UserSearch"];
これでキャッシュをデリートしているわけだし...
んー
【iOS】Core Data 2 (入力画面作成)
先日は、Core Dataでデータ登録ができるところまでで終わりました。登録できる値も固定値だったので、全然使い物にならない。
今日は、任意の文字列を登録できるよう、入力画面を作成しました。
参考資料:つくって覚えるObjective-C入門 iOS対応
http://books.ascii.jp/9784048703642/
前回のように、スナップショットを貼付けると、大量な画像になってしまうので、大事な(追加・修正を加えた)コードを主に書くことにしようかと。
まずはプロジェクトに、入力画面用のUIViewControllerサブクラスを追加。その際、xibファイル生成のチェックボックスにもチェックしておく。
まずは、入力画面用のヘッダーファイル。
#import <UIKit/UIKit.h> #import <CoreData/CoreData.h> @interface InputView : UIViewController { // 名前と電話番号 UIButton *addButton; UITextField *nameText; UITextField *phoneText; // データベースアクセス用変数を定義 NSFetchedResultsController *fetchedResultsController; } // 名前と電話番号のプロパティ // mファイル側で、@synthesizeが必要 @property (nonatomic, retain) IBOutlet UIButton *addButton; @property (nonatomic, retain) IBOutlet UITextField *nameText; @property (nonatomic, retain) IBOutlet UITextField *phoneText; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; // 登録ボタンを押した時に実行されるメソッド -(IBAction)addData; // ソフトウェアキーボードを閉じる -(IBAction)closeNameKeyboard; -(IBAction)closePhoneKeyboard; @end
氏名と電話番号の入力欄を用意して、登録用ボタンも必要。@propertyで宣言してやることで、ゲッターセッターを用意せずとも変数にアクセスできる。登録ボタン押下時のメソッドと、ソフトウェアキー入力後の閉じるアクションも用意。
上記の実装ファイルの中から、大事な箇所を抜き出し。まずは登録ボタンを押したときのメソッド。
// 登録ボタンを押した時の挙動 -(void)addData{ // insertNewObjectメソッドを参考、っていうかコピペ // コピペした際には、fetchedResultsControllerが定義されていないはずなので、要定義 // それとデフォルトでは、self.fetchedResultsController となっていたのを修正(self.をはずす) NSManagedObjectContext *context = [fetchedResultsController managedObjectContext]; NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity]; NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context]; // nameを追加する // テキストフィールドで入力した値を使用する [newManagedObject setValue:nameText.text forKey:@"name"]; // phoneNumberを追加する // テキストフィールドで入力した値を使用する [newManagedObject setValue:phoneText.text forKey:@"phoneNumber"]; NSLog(@"addData method executed."); // Save the context. NSError *error = nil; if (![context save:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } // 前画面へ戻る [self.navigationController popViewControllerAnimated:YES]; }
上のコードで、self付きのままにしてたら、クラッシュした覚えがある。
次に、氏名と電話番号入力欄に入力を終えたときの、ソフトウェアキーボードを閉じるとこ。
// 氏名入力のソフトェアキーボードを閉じる -(void)closeNameKeyboard{ [nameText resignFirstResponder]; } // 電話番号入力のソフトェアキーボードを閉じる -(void)closePhoneKeyboard{ [phoneText resignFirstResponder]; }
んで、オイラが一番悩んだところがここ。使い終わった変数をreleaseするとこ。これを正しく書かないと、入力後の画面戻り終えたとき、容赦なくクラッシュ。
- (void)dealloc { [addButton release]; [nameText release]; [phoneText release]; [fetchedResultsController release]; [super dealloc]; }
オイラはreleaseと書くべきところをついついdeallocと書いていて、クラッシュしまくってました。releaseです。
今度は、前回あったファイルに戻る(RootViewController.h)
#import <UIKit/UIKit.h> #import <CoreData/CoreData.h> #import "InputView.h" @interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate> { } // 入力画面へ移動する -(void)move2inputView; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @end
前回はなかった、入力画面へ移動するというメソッドを用意。
次に、実装ファイル(RootViewController.m)。入力画面へ移動のためのメソッド。読み込むnib(xib)の名前を書いてallocとinit。
// 入力画面へ移動、のためのメソッド -(void)move2inputView{ // tableView:didSelectRowAtIndexPath:メソッドからコピペ、参考に InputView *detailViewController = [[InputView alloc] initWithNibName:@"InputView" bundle:nil]; // 入力画面で入力してた値を、元の画面へ渡すのに必要 // 左辺 = 入力画面、右辺 = 元画面 detailViewController.fetchedResultsController = self.fetchedResultsController; [self.navigationController pushViewController:detailViewController animated:YES]; [detailViewController release]; }
ビューが読み込まれたときのメソッド。上で書いたメソッドを呼び出すように。
- (void)viewDidLoad { [super viewDidLoad]; // 編集ボタンを用意 self.navigationItem.leftBarButtonItem = self.editButtonItem; // プラスボタンを押したときの挙動 // 新規オブジェクト登録ではなく、入力画面を呼び出すように修正(move2inputView呼び出し) UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(move2inputView)]; // 新規作成ボタン(プラスボタン) self.navigationItem.rightBarButtonItem = addButton; [addButton release]; }
セルに、何を表示させるか、のメソッド。
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; // セルに、nameとphoneNumberの値を表示させる cell.textLabel.text = [managedObject valueForKey:@"name"]; cell.detailTextLabel.text = [managedObject valueForKey:@"phoneNumber"]; }
コードについてはこれくらい。Macでの開発の困難さ(簡単さ)は、ソースコードよりもむしろ、Interface Builder(以降IB)での操作に原因があると思うのだけど。なもんで、ちょいと画像を載せます。下記画像は、テキストフィールドやボタンと、変数・メソッドなどを、IBで接続しているところ。
そんなこんなで、出来ました。入力画面を作って、任意の文字列をどんどん入力できるようになりました。
さて。次に試すとしたら、どうしよう。どんどん登録したレコードをCSVにして、サーバに送信、とか?ほかにも使っていないUIがたくさんあるので、そちらを利用した場合とか。