@@ -2841,6 +2841,69 @@ async fn process_account_xls(
2841
2841
Ok ( ( ) )
2842
2842
}
2843
2843
2844
+ async fn process_account_csv (
2845
+ db : & Db ,
2846
+ outfile : & str ,
2847
+ filter_by_year : Option < i32 > ,
2848
+ ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
2849
+ use csv:: Writer ;
2850
+
2851
+ let mut wtr = Writer :: from_path ( outfile) ?;
2852
+ let mut disposed_lots = db. disposed_lots ( ) ;
2853
+ disposed_lots. sort_by_key ( |lot| lot. when ) ;
2854
+
2855
+ if let Some ( year) = filter_by_year {
2856
+ // Exclude disposed lots that were neither acquired nor disposed of in the filter year
2857
+ disposed_lots. retain ( |disposed_lot| {
2858
+ ( disposed_lot. lot . acquisition . when . year ( ) == year
2859
+ && disposed_lot. lot . income ( disposed_lot. token ) > 0. )
2860
+ || disposed_lot. when . year ( ) == year
2861
+ } )
2862
+ }
2863
+ wtr. write_record ( & [
2864
+ "Token" ,
2865
+ "Amount" ,
2866
+ "Income (USD)" ,
2867
+ "Cap Gain (USD)" ,
2868
+ "Acq. Date" ,
2869
+ "Acq. Cost (USD)" ,
2870
+ "Sale Date" ,
2871
+ "Sale Proceedings (USD)" ,
2872
+ "Acquisition Description" ,
2873
+ "Sale Description"
2874
+ ] ) ?;
2875
+
2876
+ for disposed_lot in disposed_lots {
2877
+ let mut income = disposed_lot. lot . income ( disposed_lot. token ) ;
2878
+ if let Some ( year) = filter_by_year {
2879
+ if disposed_lot. lot . acquisition . when . year ( ) != year {
2880
+ income = 0. // Exclude income from other years
2881
+ }
2882
+ }
2883
+ let cost = Decimal :: from_u64 ( disposed_lot. lot . amount ) . unwrap ( )
2884
+ * disposed_lot. lot . acquisition . price ( ) / Decimal :: from_f64 ( 1e9 ) . unwrap ( ) ;
2885
+ let proceedings = Decimal :: from_u64 ( disposed_lot. lot . amount ) . unwrap ( )
2886
+ * disposed_lot. price ( ) / Decimal :: from_f64 ( 1e9 ) . unwrap ( ) ;
2887
+ wtr. write_record ( & [
2888
+ disposed_lot. token . to_string ( ) ,
2889
+ format ! ( "{:.9}" , disposed_lot. token. ui_amount( disposed_lot. lot. amount) ) ,
2890
+ format ! ( "{:.9}" , income) ,
2891
+ format ! ( "{:.9}" , disposed_lot. lot. cap_gain( disposed_lot. token, disposed_lot. price( ) ) ) ,
2892
+ disposed_lot. lot . acquisition . when . to_string ( ) ,
2893
+ format ! ( "{:.9}" , cost) ,
2894
+ disposed_lot. when . to_string ( ) ,
2895
+ format ! ( "{:.9}" , proceedings) ,
2896
+ disposed_lot. lot . acquisition . kind . to_string ( ) ,
2897
+ disposed_lot. kind . to_string ( )
2898
+ ] ) ?;
2899
+ }
2900
+
2901
+ wtr. flush ( ) ?;
2902
+ println ! ( "Wrote {outfile}" ) ;
2903
+
2904
+ Ok ( ( ) )
2905
+ }
2906
+
2844
2907
#[ allow( clippy:: too_many_arguments) ]
2845
2908
async fn process_account_merge < T : Signers > (
2846
2909
db : & mut Db ,
@@ -4684,6 +4747,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4684
4747
. help ( "Limit export to realized gains affecting the given year" ) ,
4685
4748
) ,
4686
4749
)
4750
+ . subcommand (
4751
+ SubCommand :: with_name ( "csv" )
4752
+ . about ( "Export a CSV file" )
4753
+ . arg (
4754
+ Arg :: with_name ( "outfile" )
4755
+ . value_name ( "FILEPATH" )
4756
+ . takes_value ( true )
4757
+ . help ( ".csv file to write" ) ,
4758
+ )
4759
+ . arg (
4760
+ Arg :: with_name ( "year" )
4761
+ . long ( "year" )
4762
+ . value_name ( "YYYY" )
4763
+ . takes_value ( true )
4764
+ . validator ( is_parsable :: < usize > )
4765
+ . help ( "Limit export to realized gains affecting the given year" ) ,
4766
+ ) ,
4767
+ )
4687
4768
. subcommand (
4688
4769
SubCommand :: with_name ( "remove" )
4689
4770
. about ( "Unregister an account" )
@@ -6144,6 +6225,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
6144
6225
let filter_by_year = value_t ! ( arg_matches, "year" , i32 ) . ok ( ) ;
6145
6226
process_account_xls ( & db, & outfile, filter_by_year) . await ?;
6146
6227
}
6228
+ ( "csv" , Some ( arg_matches) ) => {
6229
+ let outfile = value_t_or_exit ! ( arg_matches, "outfile" , String ) ;
6230
+ let filter_by_year = value_t ! ( arg_matches, "year" , i32 ) . ok ( ) ;
6231
+ process_account_csv ( & db, & outfile, filter_by_year) . await ?;
6232
+ }
6147
6233
( "remove" , Some ( arg_matches) ) => {
6148
6234
let address = pubkey_of ( arg_matches, "address" ) . unwrap ( ) ;
6149
6235
let token = MaybeToken :: from ( value_t ! ( arg_matches, "token" , Token ) . ok ( ) ) ;
0 commit comments