Making WordPress.org

Changeset 2694


Ignore:
Timestamp:
03/04/2016 10:34:58 AM (11 years ago)
Author:
kovshenin
Message:

WordCamp Budgets: Move vendor payments exports to a new screet + some refactoring.

Location:
sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php

    r2668 r2694  
    1717
    1818                add_action( 'wordcamp_payments_aggregate', array( __CLASS__, 'aggregate' ) );
    19                 add_action( 'admin_enqueue_scripts',  array( __CLASS__, 'enqueue_assets' ) );
    2019                add_action( 'network_admin_menu', array( __CLASS__, 'network_admin_menu' ) );
    2120                add_action( 'init', array( __CLASS__, 'upgrade' ) );
    22                 add_action( 'init', array( __CLASS__, 'process_export_request' ) );
    2321                add_action( 'init', array( __CLASS__, 'process_import_request' ) );
    2422
     
    239237
    240238        /**
    241          * Enqueue scripts and stylesheets
    242          *
    243          * @param string $hook
    244          */
    245         public static function enqueue_assets( $hook ) {
    246                 if ( 'index_page_wcp-dashboard' == $hook && 'export' == self::get_current_tab() ) {
    247                         wp_enqueue_script( 'jquery-ui-datepicker' );
    248                         wp_enqueue_style( 'jquery-ui' );
    249                         wp_enqueue_style( 'wp-datepicker-skins' );
    250                 }
    251         }
    252 
    253         /**
    254239         * Renders the Dashboard - Payments screen.
    255240         */
     
    266251
    267252                        <?php
    268                                 if ( 'export' == self::get_current_tab() ) {
    269                                         self::render_export_tab();
    270                                 } elseif ( 'import' == self::get_current_tab() ) {
     253                                if ( 'import' == self::get_current_tab() ) {
    271254                                        self::render_import_tab();
    272255                                }
     
    381364                }
    382365                restore_current_blog();
    383         }
    384 
    385         /**
    386          * Get available export options.
    387          *
    388          * @return array
    389          */
    390         public static function get_export_types() {
    391                 return array(
    392                         'default' => array(
    393                                 'label' => 'Default',
    394                                 'mime_type' => 'text/csv',
    395                                 'callback' => array( __CLASS__, '_generate_payment_report_default' ),
    396                                 'filename' => 'wordcamp-payments-%s-%s-default.csv',
    397                         ),
    398                         'jpm_wires' => array(
    399                                 'label' => 'JP Morgan Access - Wire Payments',
    400                                 'mime_type' => 'text/csv',
    401                                 'callback' => array( __CLASS__, '_generate_payment_report_jpm_wires' ),
    402                                 'filename' => 'wordcamp-payments-%s-%s-jpm-wires.csv',
    403                         ),
    404                         'jpm_ach' => array(
    405                                 'label' => 'JP Morgan - NACHA',
    406                                 'mime_type' => 'text/plain',
    407                                 'callback' => array( __CLASS__, '_generate_payment_report_jpm_ach' ),
    408                                 'filename' => 'wordcamp-payments-%s-%s-jpm-ach.ach',
    409                         ),
    410                         'jpm_checks' => array(
    411                                 'label' => 'JP Morgan - Quick Checks',
    412                                 'mime_type' => 'text/csv',
    413                                 'callback' => array( __CLASS__, '_generate_payment_report_jpm_checks' ),
    414                                 'filename' => 'wordcamp-payments-%s-%s-jpm-checks.csv',
    415                         ),
    416                 );
    417         }
    418 
    419         /**
    420          * Process export requests
    421          */
    422         public static function process_export_request() {
    423                 if ( empty( $_POST['submit'] ) || 'export' != self::get_current_tab() ) {
    424                         return;
    425                 }
    426 
    427                 if ( ! current_user_can( 'manage_network' ) || ! check_admin_referer( 'export', 'wcpn_request_export' ) ) {
    428                         return;
    429                 }
    430 
    431                 $export_types = self::get_export_types();
    432 
    433                 if ( array_key_exists( $_POST['wcpn_export_type'], $export_types ) ) {
    434                         $export_type = $export_types[ $_POST['wcpn_export_type'] ];
    435                 } else {
    436                         $export_type = $export_types['default'];
    437                 }
    438 
    439                 $status = $_POST['wcpn_export_status'];
    440                 if ( ! in_array( $status, array( 'wcb-approved', 'wcb-paid' ) ) )
    441                         $status = 'wcb-approved';
    442 
    443                 $start_date = strtotime( $_POST['wcpn_export_start_date'] . ' 00:00:00' );
    444                 $end_date   = strtotime( $_POST['wcpn_export_end_date']   . ' 23:59:59' );
    445                 $filename = sprintf( $export_type['filename'], date( 'Ymd', $start_date ), date( 'Ymd', $end_date ) );
    446                 $filename = sanitize_file_name( $filename );
    447 
    448                 $report = self::generate_payment_report( $status, $start_date, $end_date, $export_type );
    449 
    450                 if ( is_wp_error( $report ) ) {
    451                         add_settings_error( 'wcp-dashboard', $report->get_error_code(), $report->get_error_message() );
    452                 } else {
    453                         header( sprintf( 'Content-Type: %s', $export_type['mime_type'] ) );
    454                         header( sprintf( 'Content-Disposition: attachment; filename="%s"', $filename ) );
    455                         header( 'Cache-control: private' );
    456                         header( 'Pragma: private' );
    457                         header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
    458 
    459                         echo $report;
    460                         die();
    461                 }
    462         }
    463 
    464         /*
    465          * Generate and return the raw payment report contents
    466          *
    467          * @param string $date_type 'paid' | 'created'
    468          * @param int $start_date
    469          * @param int $end_date
    470          * @param string $type
    471          *
    472          * @return string | WP_Error
    473          */
    474         protected static function generate_payment_report( $status, $start_date, $end_date, $export_type ) {
    475                 global $wpdb;
    476 
    477                 if ( ! is_int( $start_date ) || ! is_int( $end_date ) ) {
    478                         return new WP_Error( 'wcpn_bad_dates', 'Invalid start or end date.' );
    479                 }
    480 
    481                 $table_name = self::get_table_name();
    482                 $date_type = 'updated';
    483 
    484                 if ( $status == 'wcb-paid' )
    485                         $date_type = 'paid';
    486 
    487                 $request_indexes = $wpdb->get_results( $wpdb->prepare( "
    488                         SELECT *
    489                         FROM   `{$table_name}`
    490                         WHERE  `{$date_type}` BETWEEN %d AND %d",
    491                         $start_date,
    492                         $end_date
    493                 ) );
    494 
    495                 if ( ! is_callable( $export_type['callback'] ) )
    496                         return new WP_Error( 'wcpn_invalid_type', 'The export type is invalid.' );
    497 
    498                 $args = array(
    499                         'request_indexes' => $request_indexes,
    500                         'start_date' => $start_date,
    501                         'end_date' => $end_date,
    502                         'export_type' => $export_type,
    503                         'status' => $status,
    504                 );
    505 
    506                 return call_user_func( $export_type['callback'], $args );
    507         }
    508 
    509         /**
    510          * Default CSV report
    511          *
    512          * @param array $args
    513          *
    514          * @return string
    515          */
    516         protected static function _generate_payment_report_default( $args ) {
    517                 $args = wp_parse_args( $args, array(
    518                         'request_indexes' => array(),
    519                         'status' => '',
    520                 ) );
    521 
    522                 $column_headings = array(
    523                         'WordCamp', 'ID', 'Title', 'Status', 'Date Vendor was Paid', 'Creation Date', 'Due Date', 'Amount',
    524                         'Currency', 'Category', 'Payment Method','Vendor Name', 'Vendor Contact Person', 'Vendor Country',
    525                         'Check Payable To', 'URL', 'Supporting Documentation Notes',
    526                 );
    527 
    528                 ob_start();
    529                 $report = fopen( 'php://output', 'w' );
    530 
    531                 fputcsv( $report, $column_headings );
    532 
    533                 foreach( $args['request_indexes'] as $index ) {
    534                         $row = self::get_report_row( $index, $args );
    535                         if ( ! empty( $row ) ) {
    536                                 fputcsv( $report, $row );
    537                         }
    538                 }
    539 
    540                 fclose( $report );
    541                 return ob_get_clean();
    542         }
    543 
    544         /**
    545          * Quick Checks via JP Morgan
    546          *
    547          * @param array $args
    548          *
    549          * @return string
    550          */
    551         protected static function _generate_payment_report_jpm_checks( $args ) {
    552                 $args = wp_parse_args( $args, array(
    553                         'request_indexes' => array(),
    554                         'status' => '',
    555                 ) );
    556 
    557                 $options = apply_filters( 'wcb_payment_req_check_options', array(
    558                         'pws_customer_id' => '',
    559                         'account_number'  => '',
    560                         'contact_email'   => '',
    561                         'contact_phone'   => '',
    562                 ) );
    563 
    564                 $report = fopen( 'php://output', 'w' );
    565                 ob_start();
    566 
    567                 // File Header
    568                 fputcsv( $report, array( 'FILHDR', 'PWS', $options['pws_customer_id'], date( 'm/d/Y' ), date( 'Hi' ) ), ',', '|' );
    569 
    570                 $total = 0;
    571                 $count = 0;
    572 
    573                 if ( false !== get_site_transient( '_wcb_jpm_checks_counter_lock' ) ) {
    574                         wp_die( 'JPM Checks Export is locked. Please try again later or contact support.' );
    575                 }
    576 
    577                 // Avoid at least *some* race conditions.
    578                 set_site_transient( '_wcb_jpm_checks_counter_lock', 1, 30 );
    579                 $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
    580 
    581                 foreach ( $args['request_indexes'] as $index ) {
    582                         switch_to_blog( $index->blog_id );
    583                         $post = get_post( $index->post_id );
    584 
    585                         if ( $args['status'] && $post->post_status != $args['status'] )
    586                                 continue;
    587 
    588                         if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Check' )
    589                                 continue;
    590 
    591                         $count++;
    592                         $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
    593                         $total += $amount;
    594 
    595                         $payable_to = WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_payable_to', true ) );
    596                         $payable_to = html_entity_decode( $payable_to ); // J&amp;J to J&J
    597                         $countries = WordCamp_Budgets::get_valid_countries_iso3166();
    598                         $vendor_country_code = get_post_meta( $post->ID, '_camppayments_vendor_country_iso3166', true );
    599                         if ( ! empty( $countries[ $vendor_country_code ] ) ) {
    600                                 $vendor_country_code = $countries[ $vendor_country_code ]['alpha3'];
    601                         }
    602 
    603                         $description = sanitize_text_field( get_post_meta( $post->ID, '_camppayments_description', true ) );
    604                         $description = html_entity_decode( $description );
    605                         $invoice_number = get_post_meta( $post->ID, '_camppayments_invoice_number', true );
    606                         if ( ! empty( $invoice_number ) ) {
    607                                 $description = sprintf( 'Invoice %s. %s', $invoice_number, $description );
    608                         }
    609 
    610                         // Payment Header
    611                         fputcsv( $report, array(
    612                                 'PMTHDR',
    613                                 'USPS',
    614                                 'QKCHECKS',
    615                                 date( 'm/d/Y' ),
    616                                 number_format( $amount, 2, '.', '' ),
    617                                 $options['account_number'],
    618                                 $start + $count, // must be globally unique?
    619                                 $options['contact_email'],
    620                                 $options['contact_phone'],
    621                         ), ',', '|' );
    622 
    623                         // Payee Name Record
    624                         fputcsv( $report, array(
    625                                 'PAYENM',
    626                                 substr( $payable_to, 0, 35 ),
    627                                 '',
    628                                 sprintf( '%d-%d', $index->blog_id, $index->post_id ),
    629                         ), ',', '|' );
    630 
    631                         // Payee Address Record
    632                         fputcsv( $report, array(
    633                                 'PYEADD',
    634                                 substr( get_post_meta( $post->ID, '_camppayments_vendor_street_address', true ), 0, 35 ),
    635                                 '',
    636                         ), ',', '|' );
    637 
    638                         // Additional Payee Address Record
    639                         fputcsv( $report, array( 'ADDPYE', '', '' ), ',', '|' );
    640 
    641                         // Payee Postal Record
    642                         fputcsv( $report, array(
    643                                 'PYEPOS',
    644                                 substr( get_post_meta( $post->ID, '_camppayments_vendor_city', true ), 0, 35 ),
    645                                 substr( get_post_meta( $post->ID, '_camppayments_vendor_state', true ), 0, 35 ),
    646                                 substr( get_post_meta( $post->ID, '_camppayments_vendor_zip_code', true ), 0, 10 ),
    647                                 substr( $vendor_country_code, 0, 3 ),
    648                         ), ',', '|' );
    649 
    650                         // Payment Description
    651                         fputcsv( $report, array(
    652                                 'PYTDES',
    653                                 substr( $description, 0, 122 ),
    654                         ), ',', '|' );
    655 
    656                         restore_current_blog();
    657                 }
    658 
    659                 // File Trailer
    660                 fputcsv( $report, array( 'FILTRL', $count * 6 + 2 ), ',', '|' );
    661 
    662                 // Update counter and unlock
    663                 $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
    664                 update_site_option( '_wcb_jpm_checks_counter', $start + $count );
    665                 delete_site_transient( '_wcb_jpm_checks_counter_lock' );
    666 
    667                 fclose( $report );
    668                 return ob_get_clean();
    669         }
    670 
    671         /**
    672          * NACHA via JP Morgan
    673          *
    674          * @param array $args
    675          *
    676          * @return string
    677          */
    678         protected static function _generate_payment_report_jpm_ach( $args ) {
    679                 $args = wp_parse_args( $args, array(
    680                         'request_indexes' => array(),
    681                         'status' => '',
    682                 ) );
    683 
    684                 $ach_options = apply_filters( 'wcb_payment_req_ach_options', array(
    685                         'bank-routing-number' => '', // Immediate Destination (bank routing number)
    686                         'company-id'          => '', // Company ID
    687                         'financial-inst'      => '', // Originating Financial Institution
    688                 ) );
    689 
    690                 ob_start();
    691 
    692                 // File Header Record
    693 
    694                 echo '1'; // Record Type Code
    695                 echo '01'; // Priority Code
    696                 echo ' ' . str_pad( substr( $ach_options['bank-routing-number'], 0, 9 ), 9, '0', STR_PAD_LEFT );
    697                 echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10, '0', STR_PAD_LEFT ); // Immediate Origin (TIN)
    698                 echo date( 'ymd' ); // Transmission Date
    699                 echo date( 'Hi' ); // Transmission Time
    700                 echo 'A'; // File ID Modifier
    701                 echo '094'; // Record Size
    702                 echo '10'; // Blocking Factor
    703                 echo '1'; // Format Code
    704                 echo str_pad( 'JPMORGANCHASE', 23 ); // Destination
    705                 echo str_pad( 'WCEXPORT', 23 ); // Origin
    706                 echo str_pad( '', 8 ); // Reference Code (optional)
    707                 echo PHP_EOL;
    708 
    709                 // Batch Header Record
    710 
    711                 echo '5'; // Record Type Code
    712                 echo '200'; // Service Type Code
    713                 echo 'WordCamp Communi'; // Company Name
    714                 echo str_pad( '', 20 ); // Blanks
    715                 echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company Identification
    716 
    717                 // Get the first one in the set.
    718                 // @todo Split batches by account type.
    719                 foreach ( $args['request_indexes'] as $index ) {
    720                         switch_to_blog( $index->blog_id );
    721                         $post = get_post( $index->post_id );
    722                         $account_type = get_post_meta( $post->ID, '_camppayments_ach_account_type', true );
    723                         restore_current_blog();
    724 
    725                         break;
    726                 }
    727 
    728                 $entry_class = $account_type == 'Personal' ? 'PPD' : 'CCD';
    729                 echo $entry_class; // Standard Entry Class
    730 
    731                 echo 'Vendor Pay'; // Entry Description
    732                 echo date( 'ymd', self::_next_business_day_timestamp() ); // Company Description Date
    733                 echo date( 'ymd', self::_next_business_day_timestamp() ); // Effective Entry Date
    734                 echo str_pad( '', 3 ); // Blanks
    735                 echo '1'; // Originator Status Code
    736                 echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
    737                 echo '0000001'; // Batch Number
    738                 echo PHP_EOL;
    739 
    740                 $count = 0;
    741                 $total = 0;
    742                 $hash = 0;
    743 
    744                 foreach ( $args['request_indexes'] as $index ) {
    745                         switch_to_blog( $index->blog_id );
    746                         $post = get_post( $index->post_id );
    747 
    748                         if ( $args['status'] && $post->post_status != $args['status'] )
    749                                 continue;
    750 
    751                         if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Direct Deposit' )
    752                                 continue;
    753 
    754                         $count++;
    755 
    756                         // Entry Detail Record
    757 
    758                         echo '6'; // Record Type Code
    759 
    760                         $transaction_code = $account_type == 'Personal' ? '27' : '22';
    761                         echo $transaction_code; // Transaction Code
    762 
    763                         // Transit/Routing Number of Destination Bank + Check digit
    764                         $routing_number = get_post_meta( $post->ID, '_camppayments_ach_routing_number', true );
    765                         $routing_number = WCP_Encryption::maybe_decrypt( $routing_number );
    766                         $routing_number = substr( $routing_number, 0, 8 + 1 );
    767                         $routing_number = str_pad( $routing_number, 8 + 1 );
    768                         $hash += absint( substr( $routing_number, 0, 8 ) );
    769                         echo $routing_number;
    770 
    771                         // Bank Account Number
    772                         $account_number = get_post_meta( $post->ID, '_camppayments_ach_account_number', true );
    773                         $account_number = WCP_Encryption::maybe_decrypt( $account_number );
    774                         $account_number = substr( $account_number, 0, 17 );
    775                         $account_number = str_pad( $account_number, 17 );
    776                         echo $account_number;
    777 
    778                         // Amount
    779                         $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
    780                         $total += $amount;
    781                         $amount = str_pad( number_format( $amount, 2, '', '' ), 10, '0', STR_PAD_LEFT );
    782                         echo $amount;
    783 
    784                         // Individual Identification Number
    785                         echo str_pad( sprintf( '%d-%d', $index->blog_id, $index->post_id ), 15 );
    786 
    787                         // Individual Name
    788                         $name = get_post_meta( $post->ID, '_camppayments_ach_account_holder_name', true );
    789                         $name = WCP_Encryption::maybe_decrypt( $name );
    790                         $name = substr( $name, 0, 22 );
    791                         $name = str_pad( $name, 22 );
    792                         echo $name;
    793 
    794                         echo '  '; // User Defined Data
    795                         echo '0'; // Addenda Record Indicator
    796 
    797                         // Trace Number
    798                         echo str_pad( substr( $ach_options['bank-routing-number'], 0, 8 ), 8, '0', STR_PAD_LEFT ); // routing number
    799                         echo str_pad( $count, 7, '0', STR_PAD_LEFT ); // sequence number
    800                         echo PHP_EOL;
    801                 }
    802 
    803                 // Batch Trailer Record
    804 
    805                 echo '8'; // Record Type Code
    806                 echo '200'; // Service Class Code
    807                 echo str_pad( $count, 6, '0', STR_PAD_LEFT ); // Entry/Addenda Count
    808                 echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
    809                 echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
    810                 echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
    811                 echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company ID
    812                 echo str_pad( '', 25 ); // Blanks
    813                 echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
    814                 echo '0000001'; // Batch Number
    815                 echo PHP_EOL;
    816 
    817 
    818                 // File Trailer Record
    819 
    820                 echo '9'; // Record Type Code
    821                 echo '000001'; // Batch Count
    822                 echo str_pad( ceil( $count / 10 ), 6, '0', STR_PAD_LEFT ); // Block Count
    823                 echo str_pad( $count, 8, '0', STR_PAD_LEFT ); // Entry/Addenda Count
    824                 echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
    825                 echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
    826                 echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
    827                 echo str_pad( '', 39 ); // Blanks
    828                 echo PHP_EOL;
    829 
    830                 // The file must have a number of lines that is a multiple of 10 (e.g. 10, 20, 30).
    831                 echo str_repeat( PHP_EOL, 10 - ( ( 4 + $count ) % 10 ) - 1 );
    832                 return ob_get_clean();
    833         }
    834 
    835         /**
    836          * Exclude weekends and JPM holidays.
    837          *
    838          * Needs to be updated every year.
    839          *
    840          * @return int Timestamp.
    841          */
    842         private static function _next_business_day_timestamp() {
    843                 static $timestamp;
    844 
    845                 if ( isset( $timestamp ) )
    846                         return $timestamp;
    847 
    848                 $holidays = array(
    849                         date( 'Ymd', strtotime( 'Friday, January 1, 2016' ) ),
    850                         date( 'Ymd', strtotime( 'Monday, January 18, 2016' ) ),
    851                         date( 'Ymd', strtotime( 'Monday, February 15, 2016' ) ),
    852                         date( 'Ymd', strtotime( 'Monday, May 30, 2016' ) ),
    853                         date( 'Ymd', strtotime( 'Monday, July 4, 2016' ) ),
    854                         date( 'Ymd', strtotime( 'Monday, September 5, 2016' ) ),
    855                         date( 'Ymd', strtotime( 'Friday, November 11, 2016' ) ),
    856                         date( 'Ymd', strtotime( 'Thursday, November 24, 2016' ) ),
    857                         date( 'Ymd', strtotime( 'Monday, December 26, 2016' ) ),
    858                 );
    859 
    860                 $timestamp = strtotime( 'today + 1 weekday' );
    861                 $attempts = 5;
    862 
    863                 while ( in_array( date( 'Ymd', $timestamp ), $holidays ) ) {
    864                         $timestamp = strtotime( '+ 1 weekday', $timestamp );
    865                         $attempts--;
    866 
    867                         if ( ! $attempts )
    868                                 break;
    869                 }
    870 
    871                 return $timestamp;
    872         }
    873 
    874         /**
    875          * Wires via JP Morgan
    876          *
    877          * @param array $args
    878          *
    879          * @return string
    880          */
    881         protected static function _generate_payment_report_jpm_wires( $args ) {
    882                 $args = wp_parse_args( $args, array(
    883                         'request_indexes' => array(),
    884                         'status' => '',
    885                 ) );
    886 
    887                 ob_start();
    888                 $report = fopen( 'php://output', 'w' );
    889 
    890                 // JPM Header
    891                 fputcsv( $report, array( 'HEADER', gmdate( 'YmdHis' ), '1' ) );
    892 
    893                 $total = 0;
    894                 $count = 0;
    895 
    896                 foreach ( $args['request_indexes'] as $index ) {
    897                         switch_to_blog( $index->blog_id );
    898                         $post = get_post( $index->post_id );
    899 
    900                         if ( $args['status'] && $post->post_status != $args['status'] )
    901                                 continue;
    902 
    903                         // Only wires here.
    904                         if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Wire' )
    905                                 continue;
    906 
    907                         $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2);
    908                         $total += $amount;
    909                         $count += 1;
    910 
    911                         // If account starts with two letters, it's most likely an IBAN
    912                         $account = get_post_meta( $post->ID, '_camppayments_beneficiary_account_number', true );
    913                         $account = WCP_Encryption::maybe_decrypt( $account );
    914                         $account = preg_replace( '#\s#','', $account );
    915                         $account_type = preg_match( '#^[a-z]{2}#i', $account ) ? 'IBAN' : 'ACCT';
    916 
    917                         $row = array(
    918                                 '1-input-type' => 'P',
    919                                 '2-payment-method' => 'WIRES',
    920                                 '3-debit-bank-id' => apply_filters( 'wcb_payment_req_bank_id', '' ), // external file
    921                                 '4-account-number' => apply_filters( 'wcb_payment_req_bank_number', '' ), // external file
    922                                 '5-bank-to-bank' => 'N',
    923                                 '6-txn-currency' => get_post_meta( $post->ID, '_camppayments_currency', true ),
    924                                 '7-txn-amount' => $amount,
    925                                 '8-equiv-amount' => '',
    926                                 '9-clearing' => '',
    927                                 '10-ben-residence' => '',
    928                                 '11-rate-type' => '',
    929                                 '12-blank' => '',
    930                                 '13-value-date' => '',
    931 
    932                                 '14-id-type' => $account_type,
    933                                 '15-id-value' => $account,
    934                                 '16-ben-name' => substr( WCP_Encryption::maybe_decrypt(
    935                                         get_post_meta( $post->ID, '_camppayments_beneficiary_name', true ) ), 0, 35 ),
    936                                 '17-address-1' => substr( WCP_Encryption::maybe_decrypt(
    937                                         get_post_meta( $post->ID, '_camppayments_beneficiary_street_address', true ) ), 0, 35 ),
    938                                 '18-address-2' => '',
    939                                 '19-city-state-zip' => substr( sprintf( '%s %s %s',
    940                                                 WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_city', true ) ),
    941                                                 WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_state', true ) ),
    942                                                 WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_zip_code', true ) )
    943                                         ), 0, 32 ),
    944                                 '20-blank' => '',
    945                                 '21-country' => WCP_Encryption::maybe_decrypt(
    946                                         get_post_meta( $post->ID, '_camppayments_beneficiary_country_iso3166', true ) ),
    947                                 '22-blank' => '',
    948                                 '23-blank' => '',
    949 
    950                                 '24-id-type' => 'SWIFT',
    951                                 '25-id-value' => get_post_meta( $post->ID, '_camppayments_bank_bic', true ),
    952                                 '26-ben-bank-name' => substr( get_post_meta( $post->ID, '_camppayments_bank_name', true ), 0, 35 ),
    953                                 '27-ben-bank-address-1' => substr( get_post_meta( $post->ID, '_camppayments_bank_street_address', true ), 0, 35 ),
    954                                 '28-ben-bank-address-2' => '',
    955                                 '29-ben-bank-address-3' => substr( sprintf( '%s %s %s',
    956                                                 get_post_meta( $post->ID, '_camppayments_bank_city', true ),
    957                                                 get_post_meta( $post->ID, '_camppayments_bank_state', true ),
    958                                                 get_post_meta( $post->ID, '_camppayments_bank_zip_code', true )
    959                                         ), 0, 35 ),
    960                                 '30-ben-bank-country' => get_post_meta( $post->ID, '_camppayments_bank_country_iso3166', true ),
    961                                 '31-supl-id-type' => '',
    962                                 '32-supl-id-value' => '',
    963 
    964                                 '33-blank' => '',
    965                                 '34-blank' => '',
    966                                 '35-blank' => '',
    967                                 '36-blank' => '',
    968                                 '37-blank' => '',
    969                                 '38-blank' => '',
    970                                 '39-blank' => '',
    971 
    972                                 // Filled out later if not empty.
    973                                 '40-id-type' => '',
    974                                 '41-id-value' => '',
    975                                 '42-interm-bank-name' => '',
    976                                 '43-interm-bank-address-1' => '',
    977                                 '44-interm-bank-address-2' => '',
    978                                 '45-interm-bank-address-3' => '',
    979                                 '46-interm-bank-country' => '',
    980                                 '47-supl-id-type' => '',
    981                                 '48-supl-id-value' => '',
    982 
    983                                 '49-id-type' => '',
    984                                 '50-id-value' => '',
    985                                 '51-party-name' => '',
    986                                 '52-party-address-1' => '',
    987                                 '53-party-address-2' => '',
    988                                 '54-party-address-3' => '',
    989                                 '55-party-country' => '',
    990 
    991                                 '56-blank' => '',
    992                                 '57-blank' => '',
    993                                 '58-blank' => '',
    994                                 '59-blank' => '',
    995                                 '60-blank' => '',
    996                                 '61-blank' => '',
    997                                 '62-blank' => '',
    998                                 '63-blank' => '',
    999                                 '64-blank' => '',
    1000                                 '65-blank' => '',
    1001                                 '66-blank' => '',
    1002                                 '67-blank' => '',
    1003                                 '68-blank' => '',
    1004                                 '69-blank' => '',
    1005                                 '70-blank' => '',
    1006                                 '71-blank' => '',
    1007                                 '72-blank' => '',
    1008                                 '73-blank' => '',
    1009 
    1010                                 '74-ref-text' => substr( get_post_meta( $post->ID, '_camppayments_invoice_number', true ), 0, 16 ),
    1011                                 '75-internal-ref' => '',
    1012                                 '76-on-behalf-of' => '',
    1013 
    1014                                 '77-detial-1' => '',
    1015                                 '78-detial-2' => '',
    1016                                 '79-detial-3' => '',
    1017                                 '80-detail-4' => '',
    1018 
    1019                                 '81-blank' => '',
    1020                                 '82-blank' => '',
    1021                                 '83-blank' => '',
    1022                                 '84-blank' => '',
    1023                                 '85-blank' => '',
    1024                                 '86-blank' => '',
    1025                                 '87-blank' => '',
    1026                                 '88-blank' => '',
    1027 
    1028                                 '89-reporting-code' => '',
    1029                                 '90-country' => '',
    1030                                 '91-inst-1' => '',
    1031                                 '92-inst-2' => '',
    1032                                 '93-inst-3' => '',
    1033                                 '94-inst-code-1' => '',
    1034                                 '95-inst-text-1' => '',
    1035                                 '96-inst-code-2' => '',
    1036                                 '97-inst-text-2' => '',
    1037                                 '98-inst-code-3' => '',
    1038                                 '99-inst-text-3' => '',
    1039 
    1040                                 '100-stor-code-1' => '',
    1041                                 '101-stor-line-2' => '', // Hmm?
    1042                                 '102-stor-code-2' => '',
    1043                                 '103-stor-line-2' => '',
    1044                                 '104-stor-code-3' => '',
    1045                                 '105-stor-line-3' => '',
    1046                                 '106-stor-code-4' => '',
    1047                                 '107-stor-line-4' => '',
    1048                                 '108-stor-code-5' => '',
    1049                                 '109-stor-line-5' => '',
    1050                                 '110-stor-code-6' => '',
    1051                                 '111-stor-line-6' => '',
    1052 
    1053                                 '112-priority' => '',
    1054                                 '113-blank' => '',
    1055                                 '114-charges' => '',
    1056                                 '115-blank' => '',
    1057                                 '116-details' => '',
    1058                                 '117-note' => substr( sprintf( 'wcb-%d-%d', $index->blog_id, $index->post_id ), 0, 70 ),
    1059                         );
    1060 
    1061                         // If an intermediary bank is given.
    1062                         $interm_swift = get_post_meta( $post->ID, '_camppayments_interm_bank_swift', true );
    1063                         if ( ! empty( $iterm_swift ) ) {
    1064                                 $row['40-id-type'] = 'SWIFT';
    1065                                 $row['41-id-value'] = $interm_swift;
    1066 
    1067                                 $row['42-interm-bank-name'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_name', true ), 0, 35 );
    1068                                 $row['43-interm-bank-address-1'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_street_address', true ), 0, 35 );
    1069 
    1070                                 $row['44-interm-bank-address-2'] = '';
    1071                                 $row['45-interm-bank-address-3'] = substr( sprintf( '%s %s %s',
    1072                                         get_post_meta( $post->ID, '_camppayments_interm_bank_city', true ),
    1073                                         get_post_meta( $post->ID, '_camppayments_interm_bank_state', true ),
    1074                                         get_post_meta( $post->ID, '_camppayments_interm_bank_zip_code', true )
    1075                                 ), 0, 32 );
    1076 
    1077                                 $row['46-interm-bank-country'] = get_post_meta( $post->ID, '_camppayments_interm_bank_country_iso3166', true );
    1078 
    1079                                 $row['47-supl-id-type'] = 'ACCT';
    1080                                 $row['48-supl-id-value'] = get_post_meta( $post->ID, '_camppayments_interm_bank_account', true );
    1081                         }
    1082 
    1083                         // Because CSV is stupid:
    1084                         // print_r( $row );
    1085 
    1086                         fputcsv( $report, array_values( $row ) );
    1087                         restore_current_blog();
    1088                 }
    1089 
    1090                 // JPM Trailer
    1091                 fputcsv( $report, array( 'TRAILER', $count, $total ) );
    1092 
    1093                 fclose( $report );
    1094                 $results = ob_get_clean();
    1095 
    1096                 // JPM chokes on accents and non-latin characters.
    1097                 $results = remove_accents( $results );
    1098                 return $results;
    1099         }
    1100 
    1101         /**
    1102          * Gather all the request details needed for a row in the export file
    1103          *
    1104          * @param stdClass $index
    1105          * @param array $args
    1106          *
    1107          * @return array
    1108          */
    1109         protected static function get_report_row( $index, $args ) {
    1110                 switch_to_blog( $index->blog_id );
    1111 
    1112                 $request = get_post( $index->post_id );
    1113 
    1114                 $back_compat_statuses = array(
    1115                         'unpaid' => 'draft',
    1116                         'incomplete' => 'wcb-incomplete',
    1117                         'paid' => 'wcb-paid',
    1118                 );
    1119 
    1120                 // Map old statuses to new statuses.
    1121                 if ( array_key_exists( $request->post_status, $back_compat_statuses ) ) {
    1122                         $request->post_status = $back_compat_statuses[ $request->post_status ];
    1123                 }
    1124 
    1125                 if ( $args['status'] && $request->post_status != $args['status'] ) {
    1126                         return null;
    1127                 }
    1128 
    1129                 $currency         = get_post_meta( $index->post_id, '_camppayments_currency',         true );
    1130                 $category         = get_post_meta( $index->post_id, '_camppayments_payment_category', true );
    1131                 $date_vendor_paid = get_post_meta( $index->post_id, '_camppayments_date_vendor_paid', true );
    1132 
    1133                 if ( $date_vendor_paid ) {
    1134                         $date_vendor_paid = date( 'Y-m-d', $date_vendor_paid );
    1135                 }
    1136 
    1137                 if ( 'null-select-one' === $currency ) {
    1138                         $currency = '';
    1139                 }
    1140 
    1141                 if ( 'null' === $category ) {
    1142                         $category = '';
    1143                 }
    1144 
    1145                 $row = array(
    1146                         get_wordcamp_name(),
    1147                         sprintf( '%d-%d', $index->blog_id, $index->post_id ),
    1148                         $request->post_title,
    1149                         $index->status,
    1150                         $date_vendor_paid,
    1151                         date( 'Y-m-d', $index->created ),
    1152                         date( 'Y-m-d', $index->due ),
    1153                         get_post_meta( $index->post_id, '_camppayments_payment_amount', true ),
    1154                         $currency,
    1155                         $category,
    1156                         get_post_meta( $index->post_id, '_camppayments_payment_method', true ),
    1157                         get_post_meta( $index->post_id, '_camppayments_vendor_name', true ),
    1158                         get_post_meta( $index->post_id, '_camppayments_vendor_contact_person', true ),
    1159                         get_post_meta( $index->post_id, '_camppayments_vendor_country', true ),
    1160                         WCP_Encryption::maybe_decrypt( get_post_meta( $index->post_id, '_camppayments_payable_to', true ) ),
    1161                         get_edit_post_link( $index->post_id ),
    1162                         get_post_meta( $index->post_id, '_camppayments_file_notes', true ),
    1163                 );
    1164 
    1165                 restore_current_blog();
    1166 
    1167                 return $row;
    1168         }
    1169 
    1170         /**
    1171          * Render the Export tab
    1172          */
    1173         protected static function render_export_tab() {
    1174                 $today      = date( 'Y-m-d' );
    1175                 $last_month = date( 'Y-m-d', strtotime( 'now - 1 month' ) );
    1176                 ?>
    1177 
    1178                 <script>
    1179                         /**
    1180                          * Fallback to the jQueryUI datepicker if the browser doesn't support <input type="date">
    1181                          */
    1182                         jQuery( document ).ready( function( $ ) {
    1183                                 var browserTest = document.createElement( 'input' );
    1184                                 browserTest.setAttribute( 'type', 'date' );
    1185 
    1186                                 if ( 'text' === browserTest.type ) {
    1187                                         $( '#wcpn_export' ).find( 'input[type=date]' ).datepicker( {
    1188                                                 dateFormat : 'yy-mm-dd',
    1189                                                 changeMonth: true,
    1190                                                 changeYear : true
    1191                                         } );
    1192                                 }
    1193                         } );
    1194                 </script>
    1195 
    1196                 <form id="wcpn_export" method="POST">
    1197                         <?php wp_nonce_field( 'export', 'wcpn_request_export' ); ?>
    1198 
    1199                         <h2>Export Settings</h2>
    1200 
    1201                         <table class="form-table">
    1202                                 <tr>
    1203                                         <th><label>Status</label></th>
    1204                                         <td>
    1205                                                 <select name="wcpn_export_status">
    1206                                                         <option value="wcb-approved"><?php _e( 'Approved', 'wordcamporg' ); ?></option>
    1207                                                         <option value="wcb-paid"><?php _e( 'Paid', 'wordcamporg' ); ?></option>
    1208                                                 </select>
    1209                                         </td>
    1210                                 </tr>
    1211                                 <tr>
    1212                                         <th><label>Date Range</label></th>
    1213                                         <td>
    1214                                                 <input type="date" name="wcpn_export_start_date" class="medium-text" value="<?php echo esc_attr( $last_month ); ?>" /> to
    1215                                                 <input type="date" name="wcpn_export_end_date" class="medium-text" value="<?php echo esc_attr( $today ); ?>" />
    1216                                         </td>
    1217                                 </tr>
    1218                                 <tr>
    1219                                         <th><label>Format</label></th>
    1220                                         <td>
    1221                                                 <select name="wcpn_export_type">
    1222                                                         <?php foreach ( self::get_export_types() as $key => $export_type ) : ?>
    1223                                                         <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $export_type['label'] ); ?></option>
    1224                                                         <?php endforeach; ?>
    1225                                                 </select>
    1226                                         </td>
    1227                                 </tr>
    1228                         </table>
    1229 
    1230                         <?php submit_button( 'Download Export' ); ?>
    1231                 </form>
    1232 
    1233                 <?php
    1234366        }
    1235367
     
    1398530                        'incomplete',
    1399531
    1400                         'export',
    1401532                        'import',
    1402533                );
     
    1423554                        'cancelled-failed' => __( 'Cancelled/Failed', 'wordcamporg' ),
    1424555                        'incomplete'       => __( 'Incomplete', 'wordcamporg' ),
    1425                         'export'           => __( 'Export', 'wordcamporg' ),
    1426556                        'import'           => __( 'Import', 'wordcamporg' ),
    1427557                );
  • sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/wordcamp-budgets-dashboard.php

    r2535 r2694  
    88 */
    99
    10 add_action( 'network_admin_menu',    __NAMESPACE__ . '\register_budgets_menu' );
    11 add_action( 'network_admin_menu',    __NAMESPACE__ . '\remove_budgets_submenu', 11 ); // after other modules have registered their submenu pages
    12 add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts' );
     10add_action( 'network_admin_menu', __NAMESPACE__ . '\register_budgets_menu' );
     11add_action( 'network_admin_menu', __NAMESPACE__ . '\remove_budgets_submenu', 11 ); // after other modules have registered their submenu pages
     12add_action( 'network_admin_menu', __NAMESPACE__ . '\import_export_admin_menu', 11 );
     13add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts', 10, 1 );
     14
     15add_action( 'admin_init', __NAMESPACE__ . '\process_export_request' );
    1316
    1417/**
     
    3235
    3336/**
     37 * Register the Import/Export dashboard menu item.
     38 */
     39function import_export_admin_menu() {
     40        add_submenu_page(
     41                'wordcamp-budgets-dashboard',
     42                'WordCamp Budgets Import/Export',
     43                'Import/Export',
     44                'manage_network',
     45                'wcb-import-export',
     46                __NAMESPACE__ . '\render_import_export'
     47        );
     48}
     49
     50/**
     51 * Render the import/export screen.
     52 */
     53function render_import_export() {
     54        $current_tab = 'import';
     55        if ( ! empty( $_GET['section'] ) && in_array( $_GET['section'], array( 'import', 'export' ) ) ) {
     56                $current_tab = $_GET['section'];
     57        }
     58
     59        ?>
     60                <div class="wrap">
     61                        <h1>Import/Export</h1>
     62
     63                        <?php do_action( 'admin_notices' ); ?>
     64                        <?php settings_errors(); ?>
     65
     66                        <h3 class="nav-tab-wrapper">
     67                                <a class="nav-tab <?php if ( $current_tab == 'import' ) { echo 'nav-tab-active'; } ?>"
     68                                        href="<?php echo add_query_arg( array(
     69                                                'page' => 'wcb-import-export',
     70                                                'section' => 'import',
     71                                        ), network_admin_url( 'admin.php' ) ); ?>">Import</a>
     72
     73                                <a class="nav-tab <?php if ( $current_tab == 'export' ) { echo 'nav-tab-active'; } ?>"
     74                                        href="<?php echo add_query_arg( array(
     75                                                'page' => 'wcb-import-export',
     76                                                'section' => 'export',
     77                                        ), network_admin_url( 'admin.php' ) ); ?>">Export</a>
     78                        </h3>
     79
     80                        <?php
     81                                if ( 'export' == $current_tab ) {
     82                                        render_export_tab();
     83                                } elseif ( 'import' == $current_tab ) {
     84                                        render_import_tab();
     85                                }
     86                        ?>
     87
     88                </div> <!-- /wrap -->
     89        <?php
     90}
     91
     92/**
     93 * Get available export options.
     94 *
     95 * @return array
     96 */
     97function get_export_types() {
     98        return array(
     99                'default' => array(
     100                        'label' => 'Regular CSV',
     101                        'mime_type' => 'text/csv',
     102                        'callback' => __NAMESPACE__ . '\_generate_payment_report_default',
     103                        'filename' => 'wordcamp-payments-%s-%s-default.csv',
     104                ),
     105                'jpm_wires' => array(
     106                        'label' => 'JP Morgan Access - Wire Payments',
     107                        'mime_type' => 'text/csv',
     108                        'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_wires',
     109                        'filename' => 'wordcamp-payments-%s-%s-jpm-wires.csv',
     110                ),
     111                'jpm_ach' => array(
     112                        'label' => 'JP Morgan - NACHA',
     113                        'mime_type' => 'text/plain',
     114                        'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_ach',
     115                        'filename' => 'wordcamp-payments-%s-%s-jpm-ach.ach',
     116                ),
     117                'jpm_checks' => array(
     118                        'label' => 'JP Morgan - Quick Checks',
     119                        'mime_type' => 'text/csv',
     120                        'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_checks',
     121                        'filename' => 'wordcamp-payments-%s-%s-jpm-checks.csv',
     122                ),
     123        );
     124}
     125
     126/**
     127 * Render the Import tab
     128 */
     129function render_import_tab() {
     130        echo '<p>Move along, nothing to see here.</p>';
     131}
     132
     133/**
     134 * Render the export tab
     135 */
     136function render_export_tab() {
     137                $today = date( 'Y-m-d' );
     138                $last_month = date( 'Y-m-d', strtotime( 'now - 1 month' ) );
     139                ?>
     140                <script>
     141                /**
     142                 * Fallback to the jQueryUI datepicker if the browser doesn't support <input type="date">
     143                 */
     144                jQuery( document ).ready( function( $ ) {
     145                        var browserTest = document.createElement( 'input' );
     146                        browserTest.setAttribute( 'type', 'date' );
     147
     148                        if ( 'text' === browserTest.type ) {
     149                                $( '#wcpn_export' ).find( 'input[type=date]' ).datepicker( {
     150                                        dateFormat : 'yy-mm-dd',
     151                                        changeMonth: true,
     152                                        changeYear : true
     153                                } );
     154                        }
     155                } );
     156                </script>
     157
     158                <form id="wcpn_export" method="POST">
     159                        <?php wp_nonce_field( 'export', 'wcb-request-export' ); ?>
     160
     161                        <h2>Export Settings</h2>
     162
     163                        <table class="form-table">
     164                                <tr>
     165                                        <th>Types</th>
     166                                        <td>
     167                                                <label><input type="checkbox" name="wcb-export-types-vendor-payments"
     168                                                        value="1" checked disabled /> Vendor Payments</label><br />
     169                                                <label><input type="checkbox" name="wcb-export-types-reimbursements"
     170                                                        value="1" disabled /> Reimbursements</label>
     171                                        </td>
     172                                <tr>
     173                                        <th>Status</th>
     174                                        <td>
     175                                                <select name="wcb-export-status">
     176                                                        <option value="wcb-approved"><?php _e( 'Approved', 'wordcamporg' ); ?></option>
     177                                                        <option value="wcb-paid"><?php _e( 'Paid', 'wordcamporg' ); ?></option>
     178                                                </select>
     179                                        </td>
     180                                </tr>
     181                                <tr>
     182                                        <th>Date Range</th>
     183                                        <td>
     184                                                <input type="date" name="wcb-export-start-date"
     185                                                        class="medium-text" value="<?php echo esc_attr( $last_month ); ?>" /> to
     186                                                <input type="date" name="wcb-export-end-date"
     187                                                        class="medium-text" value="<?php echo esc_attr( $today ); ?>" />
     188                                        </td>
     189                                </tr>
     190                                <tr>
     191                                        <th>Format</th>
     192                                        <td>
     193                                                <select name="wcb-export-type">
     194                                                        <?php foreach ( get_export_types() as $key => $export_type ) : ?>
     195                                                        <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $export_type['label'] ); ?></option>
     196                                                        <?php endforeach; ?>
     197                                                </select>
     198                                        </td>
     199                                </tr>
     200                        </table>
     201
     202                        <?php submit_button( 'Download Export' ); ?>
     203                </form>
     204                <?php
     205}
     206
     207/**
     208 * Process export requests
     209 */
     210function process_export_request() {
     211        if ( empty( $_GET['page'] ) || $_GET['page'] != 'wcb-import-export' )
     212                return;
     213
     214        if ( empty( $_GET['section'] ) || $_GET['section'] != 'export' )
     215                return;
     216
     217        if ( empty( $_POST['wcb-request-export'] ) )
     218                return;
     219
     220        if ( ! current_user_can( 'manage_network' ) || ! check_admin_referer( 'export', 'wcb-request-export' ) )
     221                return;
     222
     223        $export_types = get_export_types();
     224
     225        if ( array_key_exists( $_POST['wcb-export-type'], $export_types ) ) {
     226                $export_type = $export_types[ $_POST['wcb-export-type'] ];
     227        } else {
     228                $export_type = $export_types['default'];
     229        }
     230
     231        $status = $_POST['wcb-export-status'];
     232        if ( ! in_array( $status, array( 'wcb-approved', 'wcb-paid' ) ) )
     233                $status = 'wcb-approved';
     234
     235        $start_date = strtotime( $_POST['wcb-export-start-date'] . ' 00:00:00' );
     236        $end_date   = strtotime( $_POST['wcb-export-end-date']   . ' 23:59:59' );
     237        $filename = sprintf( $export_type['filename'], date( 'Ymd', $start_date ), date( 'Ymd', $end_date ) );
     238        $filename = sanitize_file_name( $filename );
     239
     240        $report = generate_payment_report( array(
     241                'status' => $status,
     242                'start_date' => $start_date,
     243                'end_date' => $end_date,
     244                'export_type' => $export_type,
     245        ) );
     246
     247        if ( is_wp_error( $report ) ) {
     248                add_settings_error( 'wcb-dashboard', $report->get_error_code(), $report->get_error_message() );
     249        } else {
     250                header( sprintf( 'Content-Type: %s', $export_type['mime_type'] ) );
     251                header( sprintf( 'Content-Disposition: attachment; filename="%s"', $filename ) );
     252                header( 'Cache-control: private' );
     253                header( 'Pragma: private' );
     254                header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
     255
     256                echo $report;
     257                die();
     258        }
     259}
     260
     261/*
     262 * Generate and return the raw payment report contents
     263 *
     264 * @param array $args
     265 *
     266 * @return string | WP_Error
     267 */
     268function generate_payment_report( $args ) {
     269        global $wpdb;
     270
     271        $args = wp_parse_args( $args, array(
     272                'status'      => '',
     273                'start_date'  => '',
     274                'end_date'    => '',
     275                'export_type' => '',
     276        ) );
     277
     278        if ( ! is_int( $args['start_date'] ) || ! is_int( $args['end_date'] ) ) {
     279                return new WP_Error( 'wcb-bad-dates', 'Invalid start or end date.' );
     280        }
     281
     282        // todo: support other index tables.
     283        $table_name = $wpdb->get_blog_prefix(0) . 'wordcamp_payments_index';
     284        $date_type = 'updated';
     285
     286        if ( $args['status'] == 'wcb-paid' )
     287                $date_type = 'paid';
     288
     289        $request_indexes = $wpdb->get_results( $wpdb->prepare( "
     290                SELECT *
     291                FROM   `{$table_name}`
     292                WHERE  `{$date_type}` BETWEEN %d AND %d",
     293                $args['start_date'],
     294                $args['end_date']
     295        ) );
     296
     297        if ( ! is_callable( $args['export_type']['callback'] ) )
     298                return new \WP_Error( 'wcb-invalid-type', 'The export type is invalid.' );
     299
     300        $args['request_indexes'] = $request_indexes;
     301
     302        return call_user_func( $args['export_type']['callback'], $args );
     303}
     304
     305/**
     306 * Default CSV report
     307 *
     308 * @param array $args
     309 *
     310 * @return string
     311 */
     312function _generate_payment_report_default( $args ) {
     313        $args = wp_parse_args( $args, array(
     314                'request_indexes' => array(),
     315                'status' => '',
     316        ) );
     317
     318        $column_headings = array(
     319                'WordCamp', 'ID', 'Title', 'Status', 'Date Vendor was Paid', 'Creation Date', 'Due Date', 'Amount',
     320                'Currency', 'Category', 'Payment Method','Vendor Name', 'Vendor Contact Person', 'Vendor Country',
     321                'Check Payable To', 'URL', 'Supporting Documentation Notes',
     322        );
     323
     324        ob_start();
     325        $report = fopen( 'php://output', 'w' );
     326
     327        fputcsv( $report, $column_headings );
     328
     329        foreach( $args['request_indexes'] as $index ) {
     330                switch_to_blog( $index->blog_id );
     331
     332                $request = get_post( $index->post_id );
     333
     334                $back_compat_statuses = array(
     335                        'unpaid' => 'draft',
     336                        'incomplete' => 'wcb-incomplete',
     337                        'paid' => 'wcb-paid',
     338                );
     339
     340                // Map old statuses to new statuses.
     341                if ( array_key_exists( $request->post_status, $back_compat_statuses ) ) {
     342                        $request->post_status = $back_compat_statuses[ $request->post_status ];
     343                }
     344
     345                if ( $args['status'] && $request->post_status != $args['status'] ) {
     346                        restore_current_blog();
     347                        continue;
     348                }
     349
     350                $currency = get_post_meta( $index->post_id, '_camppayments_currency', true );
     351                $category = get_post_meta( $index->post_id, '_camppayments_payment_category', true );
     352                $date_vendor_paid = get_post_meta( $index->post_id, '_camppayments_date_vendor_paid', true );
     353
     354                if ( $date_vendor_paid ) {
     355                        $date_vendor_paid = date( 'Y-m-d', $date_vendor_paid );
     356                }
     357
     358                if ( 'null-select-one' === $currency ) {
     359                        $currency = '';
     360                }
     361
     362                if ( 'null' === $category ) {
     363                        $category = '';
     364                }
     365
     366                $row = array(
     367                        get_wordcamp_name(),
     368                        sprintf( '%d-%d', $index->blog_id, $index->post_id ),
     369                        html_entity_decode( $request->post_title ),
     370                        $index->status,
     371                        $date_vendor_paid,
     372                        date( 'Y-m-d', $index->created ),
     373                        date( 'Y-m-d', $index->due ),
     374                        get_post_meta( $index->post_id, '_camppayments_payment_amount', true ),
     375                        $currency,
     376                        $category,
     377                        get_post_meta( $index->post_id, '_camppayments_payment_method', true ),
     378                        get_post_meta( $index->post_id, '_camppayments_vendor_name', true ),
     379                        get_post_meta( $index->post_id, '_camppayments_vendor_contact_person', true ),
     380                        get_post_meta( $index->post_id, '_camppayments_vendor_country', true ),
     381                        \WCP_Encryption::maybe_decrypt( get_post_meta( $index->post_id, '_camppayments_payable_to', true ) ),
     382                        get_edit_post_link( $index->post_id ),
     383                        get_post_meta( $index->post_id, '_camppayments_file_notes', true ),
     384                );
     385
     386                restore_current_blog();
     387
     388                if ( ! empty( $row ) ) {
     389                        fputcsv( $report, $row );
     390                }
     391        }
     392
     393        fclose( $report );
     394        return ob_get_clean();
     395}
     396
     397/**
     398 * Quick Checks via JP Morgan
     399 *
     400 * @param array $args
     401 *
     402 * @return string
     403 */
     404function _generate_payment_report_jpm_checks( $args ) {
     405        $args = wp_parse_args( $args, array(
     406                'request_indexes' => array(),
     407                'status' => '',
     408        ) );
     409
     410        $options = apply_filters( 'wcb_payment_req_check_options', array(
     411                'pws_customer_id' => '',
     412                'account_number'  => '',
     413                'contact_email'   => '',
     414                'contact_phone'   => '',
     415        ) );
     416
     417        $report = fopen( 'php://output', 'w' );
     418        ob_start();
     419
     420        // File Header
     421        fputcsv( $report, array( 'FILHDR', 'PWS', $options['pws_customer_id'], date( 'm/d/Y' ), date( 'Hi' ) ), ',', '|' );
     422
     423        $total = 0;
     424        $count = 0;
     425
     426        if ( false !== get_site_transient( '_wcb_jpm_checks_counter_lock' ) ) {
     427                wp_die( 'JPM Checks Export is locked. Please try again later or contact support.' );
     428        }
     429
     430        // Avoid at least *some* race conditions.
     431        set_site_transient( '_wcb_jpm_checks_counter_lock', 1, 30 );
     432        $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
     433
     434        foreach ( $args['request_indexes'] as $index ) {
     435                switch_to_blog( $index->blog_id );
     436                $post = get_post( $index->post_id );
     437
     438                if ( $args['status'] && $post->post_status != $args['status'] ) {
     439                        restore_current_blog();
     440                        continue;
     441                }
     442
     443                if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Check' ) {
     444                        restore_current_blog();
     445                        continue;
     446                }
     447
     448                $count++;
     449                $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
     450                $total += $amount;
     451
     452                $payable_to = \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_payable_to', true ) );
     453                $payable_to = html_entity_decode( $payable_to ); // J&amp;J to J&J
     454                $countries = \WordCamp_Budgets::get_valid_countries_iso3166();
     455                $vendor_country_code = get_post_meta( $post->ID, '_camppayments_vendor_country_iso3166', true );
     456                if ( ! empty( $countries[ $vendor_country_code ] ) ) {
     457                        $vendor_country_code = $countries[ $vendor_country_code ]['alpha3'];
     458                }
     459
     460                $description = sanitize_text_field( get_post_meta( $post->ID, '_camppayments_description', true ) );
     461                $description = html_entity_decode( $description );
     462                $invoice_number = get_post_meta( $post->ID, '_camppayments_invoice_number', true );
     463                if ( ! empty( $invoice_number ) ) {
     464                        $description = sprintf( 'Invoice %s. %s', $invoice_number, $description );
     465                }
     466
     467                // Payment Header
     468                fputcsv( $report, array(
     469                        'PMTHDR',
     470                        'USPS',
     471                        'QKCHECKS',
     472                        date( 'm/d/Y' ),
     473                        number_format( $amount, 2, '.', '' ),
     474                        $options['account_number'],
     475                        $start + $count, // must be globally unique?
     476                        $options['contact_email'],
     477                        $options['contact_phone'],
     478                ), ',', '|' );
     479
     480                // Payee Name Record
     481                fputcsv( $report, array(
     482                        'PAYENM',
     483                        substr( $payable_to, 0, 35 ),
     484                        '',
     485                        sprintf( '%d-%d', $index->blog_id, $index->post_id ),
     486                ), ',', '|' );
     487
     488                // Payee Address Record
     489                fputcsv( $report, array(
     490                        'PYEADD',
     491                        substr( get_post_meta( $post->ID, '_camppayments_vendor_street_address', true ), 0, 35 ),
     492                        '',
     493                ), ',', '|' );
     494
     495                // Additional Payee Address Record
     496                fputcsv( $report, array( 'ADDPYE', '', '' ), ',', '|' );
     497
     498                // Payee Postal Record
     499                fputcsv( $report, array(
     500                        'PYEPOS',
     501                        substr( get_post_meta( $post->ID, '_camppayments_vendor_city', true ), 0, 35 ),
     502                        substr( get_post_meta( $post->ID, '_camppayments_vendor_state', true ), 0, 35 ),
     503                        substr( get_post_meta( $post->ID, '_camppayments_vendor_zip_code', true ), 0, 10 ),
     504                        substr( $vendor_country_code, 0, 3 ),
     505                ), ',', '|' );
     506
     507                // Payment Description
     508                fputcsv( $report, array(
     509                        'PYTDES',
     510                        substr( $description, 0, 122 ),
     511                ), ',', '|' );
     512
     513                restore_current_blog();
     514        }
     515
     516        // File Trailer
     517        fputcsv( $report, array( 'FILTRL', $count * 6 + 2 ), ',', '|' );
     518
     519        // Update counter and unlock
     520        $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
     521        update_site_option( '_wcb_jpm_checks_counter', $start + $count );
     522        delete_site_transient( '_wcb_jpm_checks_counter_lock' );
     523
     524        fclose( $report );
     525        return ob_get_clean();
     526}
     527
     528/**
     529 * NACHA via JP Morgan
     530 *
     531 * @param array $args
     532 *
     533 * @return string
     534 */
     535function _generate_payment_report_jpm_ach( $args ) {
     536        $args = wp_parse_args( $args, array(
     537                'request_indexes' => array(),
     538                'status' => '',
     539        ) );
     540
     541        $ach_options = apply_filters( 'wcb_payment_req_ach_options', array(
     542                'bank-routing-number' => '', // Immediate Destination (bank routing number)
     543                'company-id'          => '', // Company ID
     544                'financial-inst'      => '', // Originating Financial Institution
     545        ) );
     546
     547        ob_start();
     548
     549        // File Header Record
     550
     551        echo '1'; // Record Type Code
     552        echo '01'; // Priority Code
     553        echo ' ' . str_pad( substr( $ach_options['bank-routing-number'], 0, 9 ), 9, '0', STR_PAD_LEFT );
     554        echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10, '0', STR_PAD_LEFT ); // Immediate Origin (TIN)
     555        echo date( 'ymd' ); // Transmission Date
     556        echo date( 'Hi' ); // Transmission Time
     557        echo 'A'; // File ID Modifier
     558        echo '094'; // Record Size
     559        echo '10'; // Blocking Factor
     560        echo '1'; // Format Code
     561        echo str_pad( 'JPMORGANCHASE', 23 ); // Destination
     562        echo str_pad( 'WCEXPORT', 23 ); // Origin
     563        echo str_pad( '', 8 ); // Reference Code (optional)
     564        echo PHP_EOL;
     565
     566        // Batch Header Record
     567
     568        echo '5'; // Record Type Code
     569        echo '200'; // Service Type Code
     570        echo 'WordCamp Communi'; // Company Name
     571        echo str_pad( '', 20 ); // Blanks
     572        echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company Identification
     573
     574        // Get the first one in the set.
     575        // @todo Split batches by account type.
     576        foreach ( $args['request_indexes'] as $index ) {
     577                switch_to_blog( $index->blog_id );
     578                $post = get_post( $index->post_id );
     579                $account_type = get_post_meta( $post->ID, '_camppayments_ach_account_type', true );
     580                restore_current_blog();
     581
     582                break;
     583        }
     584
     585        $entry_class = $account_type == 'Personal' ? 'PPD' : 'CCD';
     586        echo $entry_class; // Standard Entry Class
     587
     588        echo 'Vendor Pay'; // Entry Description
     589        echo date( 'ymd', _next_business_day_timestamp() ); // Company Description Date
     590        echo date( 'ymd', _next_business_day_timestamp() ); // Effective Entry Date
     591        echo str_pad( '', 3 ); // Blanks
     592        echo '1'; // Originator Status Code
     593        echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
     594        echo '0000001'; // Batch Number
     595        echo PHP_EOL;
     596
     597        $count = 0;
     598        $total = 0;
     599        $hash = 0;
     600
     601        foreach ( $args['request_indexes'] as $index ) {
     602                switch_to_blog( $index->blog_id );
     603                $post = get_post( $index->post_id );
     604
     605                if ( $args['status'] && $post->post_status != $args['status'] ) {
     606                        restore_current_blog();
     607                        continue;
     608                }
     609
     610                if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Direct Deposit' ) {
     611                        restore_current_blog();
     612                        continue;
     613                }
     614
     615                $count++;
     616
     617                // Entry Detail Record
     618
     619                echo '6'; // Record Type Code
     620
     621                $transaction_code = $account_type == 'Personal' ? '27' : '22';
     622                echo $transaction_code; // Transaction Code
     623
     624                // Transit/Routing Number of Destination Bank + Check digit
     625                $routing_number = get_post_meta( $post->ID, '_camppayments_ach_routing_number', true );
     626                $routing_number = \WCP_Encryption::maybe_decrypt( $routing_number );
     627                $routing_number = substr( $routing_number, 0, 8 + 1 );
     628                $routing_number = str_pad( $routing_number, 8 + 1 );
     629                $hash += absint( substr( $routing_number, 0, 8 ) );
     630                echo $routing_number;
     631
     632                // Bank Account Number
     633                $account_number = get_post_meta( $post->ID, '_camppayments_ach_account_number', true );
     634                $account_number = \WCP_Encryption::maybe_decrypt( $account_number );
     635                $account_number = substr( $account_number, 0, 17 );
     636                $account_number = str_pad( $account_number, 17 );
     637                echo $account_number;
     638
     639                // Amount
     640                $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
     641                $total += $amount;
     642                $amount = str_pad( number_format( $amount, 2, '', '' ), 10, '0', STR_PAD_LEFT );
     643                echo $amount;
     644
     645                // Individual Identification Number
     646                echo str_pad( sprintf( '%d-%d', $index->blog_id, $index->post_id ), 15 );
     647
     648                // Individual Name
     649                $name = get_post_meta( $post->ID, '_camppayments_ach_account_holder_name', true );
     650                $name = \WCP_Encryption::maybe_decrypt( $name );
     651                $name = substr( $name, 0, 22 );
     652                $name = str_pad( $name, 22 );
     653                echo $name;
     654
     655                echo '  '; // User Defined Data
     656                echo '0'; // Addenda Record Indicator
     657
     658                // Trace Number
     659                echo str_pad( substr( $ach_options['bank-routing-number'], 0, 8 ), 8, '0', STR_PAD_LEFT ); // routing number
     660                echo str_pad( $count, 7, '0', STR_PAD_LEFT ); // sequence number
     661                echo PHP_EOL;
     662        }
     663
     664        // Batch Trailer Record
     665
     666        echo '8'; // Record Type Code
     667        echo '200'; // Service Class Code
     668        echo str_pad( $count, 6, '0', STR_PAD_LEFT ); // Entry/Addenda Count
     669        echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
     670        echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
     671        echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
     672        echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company ID
     673        echo str_pad( '', 25 ); // Blanks
     674        echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
     675        echo '0000001'; // Batch Number
     676        echo PHP_EOL;
     677
     678
     679        // File Trailer Record
     680
     681        echo '9'; // Record Type Code
     682        echo '000001'; // Batch Count
     683        echo str_pad( ceil( $count / 10 ), 6, '0', STR_PAD_LEFT ); // Block Count
     684        echo str_pad( $count, 8, '0', STR_PAD_LEFT ); // Entry/Addenda Count
     685        echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
     686        echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
     687        echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
     688        echo str_pad( '', 39 ); // Blanks
     689        echo PHP_EOL;
     690
     691        // The file must have a number of lines that is a multiple of 10 (e.g. 10, 20, 30).
     692        echo str_repeat( PHP_EOL, 10 - ( ( 4 + $count ) % 10 ) - 1 );
     693        return ob_get_clean();
     694}
     695
     696/**
     697 * Wires via JP Morgan
     698 *
     699 * @param array $args
     700 *
     701 * @return string
     702 */
     703function _generate_payment_report_jpm_wires( $args ) {
     704        $args = wp_parse_args( $args, array(
     705                'request_indexes' => array(),
     706                'status' => '',
     707        ) );
     708
     709        ob_start();
     710        $report = fopen( 'php://output', 'w' );
     711
     712        // JPM Header
     713        fputcsv( $report, array( 'HEADER', gmdate( 'YmdHis' ), '1' ) );
     714
     715        $total = 0;
     716        $count = 0;
     717
     718        foreach ( $args['request_indexes'] as $index ) {
     719                switch_to_blog( $index->blog_id );
     720                $post = get_post( $index->post_id );
     721
     722                if ( $args['status'] && $post->post_status != $args['status'] ) {
     723                        restore_current_blog();
     724                        continue;
     725                }
     726
     727                // Only wires here.
     728                if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Wire' ) {
     729                        restore_current_blog();
     730                        continue;
     731                }
     732
     733                $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2);
     734                $total += $amount;
     735                $count += 1;
     736
     737                // If account starts with two letters, it's most likely an IBAN
     738                $account = get_post_meta( $post->ID, '_camppayments_beneficiary_account_number', true );
     739                $account = \WCP_Encryption::maybe_decrypt( $account );
     740                $account = preg_replace( '#\s#','', $account );
     741                $account_type = preg_match( '#^[a-z]{2}#i', $account ) ? 'IBAN' : 'ACCT';
     742
     743                $row = array(
     744                        '1-input-type' => 'P',
     745                        '2-payment-method' => 'WIRES',
     746                        '3-debit-bank-id' => apply_filters( 'wcb_payment_req_bank_id', '' ), // external file
     747                        '4-account-number' => apply_filters( 'wcb_payment_req_bank_number', '' ), // external file
     748                        '5-bank-to-bank' => 'N',
     749                        '6-txn-currency' => get_post_meta( $post->ID, '_camppayments_currency', true ),
     750                        '7-txn-amount' => number_format( $amount, 2, '.', '' ),
     751                        '8-equiv-amount' => '',
     752                        '9-clearing' => '',
     753                        '10-ben-residence' => '',
     754                        '11-rate-type' => '',
     755                        '12-blank' => '',
     756                        '13-value-date' => '',
     757
     758                        '14-id-type' => $account_type,
     759                        '15-id-value' => $account,
     760                        '16-ben-name' => substr( \WCP_Encryption::maybe_decrypt(
     761                                get_post_meta( $post->ID, '_camppayments_beneficiary_name', true ) ), 0, 35 ),
     762                        '17-address-1' => substr( \WCP_Encryption::maybe_decrypt(
     763                                get_post_meta( $post->ID, '_camppayments_beneficiary_street_address', true ) ), 0, 35 ),
     764                        '18-address-2' => '',
     765                        '19-city-state-zip' => substr( sprintf( '%s %s %s',
     766                                        \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_city', true ) ),
     767                                        \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_state', true ) ),
     768                                        \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_zip_code', true ) )
     769                                ), 0, 32 ),
     770                        '20-blank' => '',
     771                        '21-country' => \WCP_Encryption::maybe_decrypt(
     772                                get_post_meta( $post->ID, '_camppayments_beneficiary_country_iso3166', true ) ),
     773                        '22-blank' => '',
     774                        '23-blank' => '',
     775
     776                        '24-id-type' => 'SWIFT',
     777                        '25-id-value' => get_post_meta( $post->ID, '_camppayments_bank_bic', true ),
     778                        '26-ben-bank-name' => substr( get_post_meta( $post->ID, '_camppayments_bank_name', true ), 0, 35 ),
     779                        '27-ben-bank-address-1' => substr( get_post_meta( $post->ID, '_camppayments_bank_street_address', true ), 0, 35 ),
     780                        '28-ben-bank-address-2' => '',
     781                        '29-ben-bank-address-3' => substr( sprintf( '%s %s %s',
     782                                        get_post_meta( $post->ID, '_camppayments_bank_city', true ),
     783                                        get_post_meta( $post->ID, '_camppayments_bank_state', true ),
     784                                        get_post_meta( $post->ID, '_camppayments_bank_zip_code', true )
     785                                 ), 0, 35 ),
     786                        '30-ben-bank-country' => get_post_meta( $post->ID, '_camppayments_bank_country_iso3166', true ),
     787                        '31-supl-id-type' => '',
     788                        '32-supl-id-value' => '',
     789
     790                        '33-blank' => '',
     791                        '34-blank' => '',
     792                        '35-blank' => '',
     793                        '36-blank' => '',
     794                        '37-blank' => '',
     795                        '38-blank' => '',
     796                        '39-blank' => '',
     797
     798                        // Filled out later if not empty.
     799                        '40-id-type' => '',
     800                        '41-id-value' => '',
     801                        '42-interm-bank-name' => '',
     802                        '43-interm-bank-address-1' => '',
     803                        '44-interm-bank-address-2' => '',
     804                        '45-interm-bank-address-3' => '',
     805                        '46-interm-bank-country' => '',
     806                        '47-supl-id-type' => '',
     807                        '48-supl-id-value' => '',
     808
     809                        '49-id-type' => '',
     810                        '50-id-value' => '',
     811                        '51-party-name' => '',
     812                        '52-party-address-1' => '',
     813                        '53-party-address-2' => '',
     814                        '54-party-address-3' => '',
     815                        '55-party-country' => '',
     816
     817                        '56-blank' => '',
     818                        '57-blank' => '',
     819                        '58-blank' => '',
     820                        '59-blank' => '',
     821                        '60-blank' => '',
     822                        '61-blank' => '',
     823                        '62-blank' => '',
     824                        '63-blank' => '',
     825                        '64-blank' => '',
     826                        '65-blank' => '',
     827                        '66-blank' => '',
     828                        '67-blank' => '',
     829                        '68-blank' => '',
     830                        '69-blank' => '',
     831                        '70-blank' => '',
     832                        '71-blank' => '',
     833                        '72-blank' => '',
     834                        '73-blank' => '',
     835
     836                        '74-ref-text' => substr( get_post_meta( $post->ID, '_camppayments_invoice_number', true ), 0, 16 ),
     837                        '75-internal-ref' => '',
     838                        '76-on-behalf-of' => '',
     839
     840                        '77-detial-1' => '',
     841                        '78-detial-2' => '',
     842                        '79-detial-3' => '',
     843                        '80-detail-4' => '',
     844
     845                        '81-blank' => '',
     846                        '82-blank' => '',
     847                        '83-blank' => '',
     848                        '84-blank' => '',
     849                        '85-blank' => '',
     850                        '86-blank' => '',
     851                        '87-blank' => '',
     852                        '88-blank' => '',
     853
     854                        '89-reporting-code' => '',
     855                        '90-country' => '',
     856                        '91-inst-1' => '',
     857                        '92-inst-2' => '',
     858                        '93-inst-3' => '',
     859                        '94-inst-code-1' => '',
     860                        '95-inst-text-1' => '',
     861                        '96-inst-code-2' => '',
     862                        '97-inst-text-2' => '',
     863                        '98-inst-code-3' => '',
     864                        '99-inst-text-3' => '',
     865
     866                        '100-stor-code-1' => '',
     867                        '101-stor-line-2' => '', // Hmm?
     868                        '102-stor-code-2' => '',
     869                        '103-stor-line-2' => '',
     870                        '104-stor-code-3' => '',
     871                        '105-stor-line-3' => '',
     872                        '106-stor-code-4' => '',
     873                        '107-stor-line-4' => '',
     874                        '108-stor-code-5' => '',
     875                        '109-stor-line-5' => '',
     876                        '110-stor-code-6' => '',
     877                        '111-stor-line-6' => '',
     878
     879                        '112-priority' => '',
     880                        '113-blank' => '',
     881                        '114-charges' => '',
     882                        '115-blank' => '',
     883                        '116-details' => '',
     884                        '117-note' => substr( sprintf( 'wcb-%d-%d', $index->blog_id, $index->post_id ), 0, 70 ),
     885                );
     886
     887                // If an intermediary bank is given.
     888                $interm_swift = get_post_meta( $post->ID, '_camppayments_interm_bank_swift', true );
     889                if ( ! empty( $iterm_swift ) ) {
     890                        $row['40-id-type'] = 'SWIFT';
     891                        $row['41-id-value'] = $interm_swift;
     892
     893                        $row['42-interm-bank-name'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_name', true ), 0, 35 );
     894                        $row['43-interm-bank-address-1'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_street_address', true ), 0, 35 );
     895
     896                        $row['44-interm-bank-address-2'] = '';
     897                        $row['45-interm-bank-address-3'] = substr( sprintf( '%s %s %s',
     898                                get_post_meta( $post->ID, '_camppayments_interm_bank_city', true ),
     899                                get_post_meta( $post->ID, '_camppayments_interm_bank_state', true ),
     900                                get_post_meta( $post->ID, '_camppayments_interm_bank_zip_code', true )
     901                        ), 0, 32 );
     902
     903                        $row['46-interm-bank-country'] = get_post_meta( $post->ID, '_camppayments_interm_bank_country_iso3166', true );
     904
     905                        $row['47-supl-id-type'] = 'ACCT';
     906                        $row['48-supl-id-value'] = get_post_meta( $post->ID, '_camppayments_interm_bank_account', true );
     907                }
     908
     909                // Use for debugging.
     910                // print_r( $row );
     911
     912                fputcsv( $report, array_values( $row ) );
     913                restore_current_blog();
     914        }
     915
     916        // JPM Trailer
     917        fputcsv( $report, array( 'TRAILER', $count, $total ) );
     918
     919        fclose( $report );
     920        $results = ob_get_clean();
     921
     922        // JPM chokes on accents and non-latin characters.
     923        $results = remove_accents( $results );
     924        return $results;
     925}
     926
     927/**
     928 * Exclude weekends and JPM holidays.
     929 *
     930 * Needs to be updated every year.
     931 *
     932 * @return int Timestamp.
     933 */
     934function _next_business_day_timestamp() {
     935        static $timestamp;
     936
     937        if ( isset( $timestamp ) )
     938                return $timestamp;
     939
     940        $holidays = array(
     941                date( 'Ymd', strtotime( 'Friday, January 1, 2016' ) ),
     942                date( 'Ymd', strtotime( 'Monday, January 18, 2016' ) ),
     943                date( 'Ymd', strtotime( 'Monday, February 15, 2016' ) ),
     944                date( 'Ymd', strtotime( 'Monday, May 30, 2016' ) ),
     945                date( 'Ymd', strtotime( 'Monday, July 4, 2016' ) ),
     946                date( 'Ymd', strtotime( 'Monday, September 5, 2016' ) ),
     947                date( 'Ymd', strtotime( 'Friday, November 11, 2016' ) ),
     948                date( 'Ymd', strtotime( 'Thursday, November 24, 2016' ) ),
     949                date( 'Ymd', strtotime( 'Monday, December 26, 2016' ) ),
     950        );
     951
     952        $timestamp = strtotime( 'today + 1 weekday' );
     953        $attempts = 5;
     954
     955        while ( in_array( date( 'Ymd', $timestamp ), $holidays ) ) {
     956                $timestamp = strtotime( '+ 1 weekday', $timestamp );
     957                $attempts--;
     958
     959                if ( ! $attempts )
     960                        break;
     961        }
     962
     963        return $timestamp;
     964}
     965
     966/**
    34967 * Remove the empty Budgets submenu item
    35968 *
     
    37970 */
    38971function remove_budgets_submenu() {
    39         remove_submenu_page( 'wordcamp-budgets-dashboard', 'wordcamp-budgets-dashboard' );
     972        remove_submenu_page( 'wordcamp-budgets-dashboard', 'wordcamp-budgets-dashboard' );
    40973}
    41974
     
    43976 * Enqueue scripts and styles
    44977 */
    45 function enqueue_scripts() {
     978function enqueue_scripts( $hook ) {
    46979        wp_enqueue_style(
    47980                'wordcamp-budgets-dashboard',
     
    50983                1
    51984        );
     985
     986        if ( $hook == 'budgets_page_wcb-import-export' ) {
     987                wp_enqueue_script( 'jquery-ui-datepicker' );
     988                wp_enqueue_style( 'jquery-ui' );
     989                wp_enqueue_style( 'wp-datepicker-skins' );
     990        }
    52991}
    53992
     
    621001function format_amount( $amount, $currency ) {
    631002        $formatted_amount = '';
    64         $amount           = \WordCamp_Budgets::validate_amount( $amount );
     1003        $amount = \WordCamp_Budgets::validate_amount( $amount );
    651004
    661005        if ( false === strpos( $currency, 'null' ) && $amount ) {
     
    941033        global $wpdb;
    951034
    96         $from      = strtolower( $from );
    97         $to        = strtolower( $to );
     1035        $from = strtolower( $from );
     1036        $to = strtolower( $to );
    981037        $cache_key = md5( sprintf( 'wcp-exchange-rate-%s:%s', $from, $to ) );
    991038
     
    1071046
    1081047                $request = wp_remote_get( esc_url_raw( $url ) );
    109                 $body    = json_decode( wp_remote_retrieve_body( $request ), true );
     1048                $body = json_decode( wp_remote_retrieve_body( $request ), true );
    1101049
    1111050                if ( ! empty( $body['query']['results']['rate']['Ask'] ) ) {
Note: See TracChangeset for help on using the changeset viewer.