Making WordPress.org

Changeset 2694


Ignore:
Timestamp:
03/04/2016 10:34:58 AM (10 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.