<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[2694] sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes: WordCamp Budgets: Move vendor payments exports to a new screet + some refactoring.</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/2694">2694</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/2694","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>kovshenin</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-03-04 10:34:58 +0000 (Fri, 04 Mar 2016)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>WordCamp Budgets: Move vendor payments exports to a new screet + some refactoring.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsnetworkincludespaymentrequestsdashboardphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsnetworkincludeswordcampbudgetsdashboardphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/wordcamp-budgets-dashboard.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsnetworkincludespaymentrequestsdashboardphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php 2016-03-03 23:05:06 UTC (rev 2693)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/payment-requests-dashboard.php 2016-03-04 10:34:58 UTC (rev 2694)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -16,10 +16,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> wp_schedule_event( time(), 'hourly', 'wordcamp_payments_aggregate' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wordcamp_payments_aggregate', array( __CLASS__, 'aggregate' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
</del><span class="cx" style="display: block; padding: 0 10px"> add_action( 'network_admin_menu', array( __CLASS__, 'network_admin_menu' ) );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'init', array( __CLASS__, 'upgrade' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- add_action( 'init', array( __CLASS__, 'process_export_request' ) );
</del><span class="cx" style="display: block; padding: 0 10px"> add_action( 'init', array( __CLASS__, 'process_import_request' ) );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Dashboard actions.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -238,19 +236,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Enqueue scripts and stylesheets
- *
- * @param string $hook
- */
- public static function enqueue_assets( $hook ) {
- if ( 'index_page_wcp-dashboard' == $hook && 'export' == self::get_current_tab() ) {
- wp_enqueue_script( 'jquery-ui-datepicker' );
- wp_enqueue_style( 'jquery-ui' );
- wp_enqueue_style( 'wp-datepicker-skins' );
- }
- }
-
- /**
</del><span class="cx" style="display: block; padding: 0 10px"> * Renders the Dashboard - Payments screen.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function render_dashboard() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -265,9 +250,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <h3 class="nav-tab-wrapper"><?php self::render_dashboard_tabs(); ?></h3>
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( 'export' == self::get_current_tab() ) {
- self::render_export_tab();
- } elseif ( 'import' == self::get_current_tab() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'import' == self::get_current_tab() ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> self::render_import_tab();
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> else {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -383,857 +366,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Get available export options.
- *
- * @return array
- */
- public static function get_export_types() {
- return array(
- 'default' => array(
- 'label' => 'Default',
- 'mime_type' => 'text/csv',
- 'callback' => array( __CLASS__, '_generate_payment_report_default' ),
- 'filename' => 'wordcamp-payments-%s-%s-default.csv',
- ),
- 'jpm_wires' => array(
- 'label' => 'JP Morgan Access - Wire Payments',
- 'mime_type' => 'text/csv',
- 'callback' => array( __CLASS__, '_generate_payment_report_jpm_wires' ),
- 'filename' => 'wordcamp-payments-%s-%s-jpm-wires.csv',
- ),
- 'jpm_ach' => array(
- 'label' => 'JP Morgan - NACHA',
- 'mime_type' => 'text/plain',
- 'callback' => array( __CLASS__, '_generate_payment_report_jpm_ach' ),
- 'filename' => 'wordcamp-payments-%s-%s-jpm-ach.ach',
- ),
- 'jpm_checks' => array(
- 'label' => 'JP Morgan - Quick Checks',
- 'mime_type' => 'text/csv',
- 'callback' => array( __CLASS__, '_generate_payment_report_jpm_checks' ),
- 'filename' => 'wordcamp-payments-%s-%s-jpm-checks.csv',
- ),
- );
- }
-
- /**
- * Process export requests
- */
- public static function process_export_request() {
- if ( empty( $_POST['submit'] ) || 'export' != self::get_current_tab() ) {
- return;
- }
-
- if ( ! current_user_can( 'manage_network' ) || ! check_admin_referer( 'export', 'wcpn_request_export' ) ) {
- return;
- }
-
- $export_types = self::get_export_types();
-
- if ( array_key_exists( $_POST['wcpn_export_type'], $export_types ) ) {
- $export_type = $export_types[ $_POST['wcpn_export_type'] ];
- } else {
- $export_type = $export_types['default'];
- }
-
- $status = $_POST['wcpn_export_status'];
- if ( ! in_array( $status, array( 'wcb-approved', 'wcb-paid' ) ) )
- $status = 'wcb-approved';
-
- $start_date = strtotime( $_POST['wcpn_export_start_date'] . ' 00:00:00' );
- $end_date = strtotime( $_POST['wcpn_export_end_date'] . ' 23:59:59' );
- $filename = sprintf( $export_type['filename'], date( 'Ymd', $start_date ), date( 'Ymd', $end_date ) );
- $filename = sanitize_file_name( $filename );
-
- $report = self::generate_payment_report( $status, $start_date, $end_date, $export_type );
-
- if ( is_wp_error( $report ) ) {
- add_settings_error( 'wcp-dashboard', $report->get_error_code(), $report->get_error_message() );
- } else {
- header( sprintf( 'Content-Type: %s', $export_type['mime_type'] ) );
- header( sprintf( 'Content-Disposition: attachment; filename="%s"', $filename ) );
- header( 'Cache-control: private' );
- header( 'Pragma: private' );
- header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
-
- echo $report;
- die();
- }
- }
-
- /*
- * Generate and return the raw payment report contents
- *
- * @param string $date_type 'paid' | 'created'
- * @param int $start_date
- * @param int $end_date
- * @param string $type
- *
- * @return string | WP_Error
- */
- protected static function generate_payment_report( $status, $start_date, $end_date, $export_type ) {
- global $wpdb;
-
- if ( ! is_int( $start_date ) || ! is_int( $end_date ) ) {
- return new WP_Error( 'wcpn_bad_dates', 'Invalid start or end date.' );
- }
-
- $table_name = self::get_table_name();
- $date_type = 'updated';
-
- if ( $status == 'wcb-paid' )
- $date_type = 'paid';
-
- $request_indexes = $wpdb->get_results( $wpdb->prepare( "
- SELECT *
- FROM `{$table_name}`
- WHERE `{$date_type}` BETWEEN %d AND %d",
- $start_date,
- $end_date
- ) );
-
- if ( ! is_callable( $export_type['callback'] ) )
- return new WP_Error( 'wcpn_invalid_type', 'The export type is invalid.' );
-
- $args = array(
- 'request_indexes' => $request_indexes,
- 'start_date' => $start_date,
- 'end_date' => $end_date,
- 'export_type' => $export_type,
- 'status' => $status,
- );
-
- return call_user_func( $export_type['callback'], $args );
- }
-
- /**
- * Default CSV report
- *
- * @param array $args
- *
- * @return string
- */
- protected static function _generate_payment_report_default( $args ) {
- $args = wp_parse_args( $args, array(
- 'request_indexes' => array(),
- 'status' => '',
- ) );
-
- $column_headings = array(
- 'WordCamp', 'ID', 'Title', 'Status', 'Date Vendor was Paid', 'Creation Date', 'Due Date', 'Amount',
- 'Currency', 'Category', 'Payment Method','Vendor Name', 'Vendor Contact Person', 'Vendor Country',
- 'Check Payable To', 'URL', 'Supporting Documentation Notes',
- );
-
- ob_start();
- $report = fopen( 'php://output', 'w' );
-
- fputcsv( $report, $column_headings );
-
- foreach( $args['request_indexes'] as $index ) {
- $row = self::get_report_row( $index, $args );
- if ( ! empty( $row ) ) {
- fputcsv( $report, $row );
- }
- }
-
- fclose( $report );
- return ob_get_clean();
- }
-
- /**
- * Quick Checks via JP Morgan
- *
- * @param array $args
- *
- * @return string
- */
- protected static function _generate_payment_report_jpm_checks( $args ) {
- $args = wp_parse_args( $args, array(
- 'request_indexes' => array(),
- 'status' => '',
- ) );
-
- $options = apply_filters( 'wcb_payment_req_check_options', array(
- 'pws_customer_id' => '',
- 'account_number' => '',
- 'contact_email' => '',
- 'contact_phone' => '',
- ) );
-
- $report = fopen( 'php://output', 'w' );
- ob_start();
-
- // File Header
- fputcsv( $report, array( 'FILHDR', 'PWS', $options['pws_customer_id'], date( 'm/d/Y' ), date( 'Hi' ) ), ',', '|' );
-
- $total = 0;
- $count = 0;
-
- if ( false !== get_site_transient( '_wcb_jpm_checks_counter_lock' ) ) {
- wp_die( 'JPM Checks Export is locked. Please try again later or contact support.' );
- }
-
- // Avoid at least *some* race conditions.
- set_site_transient( '_wcb_jpm_checks_counter_lock', 1, 30 );
- $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
-
- foreach ( $args['request_indexes'] as $index ) {
- switch_to_blog( $index->blog_id );
- $post = get_post( $index->post_id );
-
- if ( $args['status'] && $post->post_status != $args['status'] )
- continue;
-
- if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Check' )
- continue;
-
- $count++;
- $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
- $total += $amount;
-
- $payable_to = WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_payable_to', true ) );
- $payable_to = html_entity_decode( $payable_to ); // J&J to J&J
- $countries = WordCamp_Budgets::get_valid_countries_iso3166();
- $vendor_country_code = get_post_meta( $post->ID, '_camppayments_vendor_country_iso3166', true );
- if ( ! empty( $countries[ $vendor_country_code ] ) ) {
- $vendor_country_code = $countries[ $vendor_country_code ]['alpha3'];
- }
-
- $description = sanitize_text_field( get_post_meta( $post->ID, '_camppayments_description', true ) );
- $description = html_entity_decode( $description );
- $invoice_number = get_post_meta( $post->ID, '_camppayments_invoice_number', true );
- if ( ! empty( $invoice_number ) ) {
- $description = sprintf( 'Invoice %s. %s', $invoice_number, $description );
- }
-
- // Payment Header
- fputcsv( $report, array(
- 'PMTHDR',
- 'USPS',
- 'QKCHECKS',
- date( 'm/d/Y' ),
- number_format( $amount, 2, '.', '' ),
- $options['account_number'],
- $start + $count, // must be globally unique?
- $options['contact_email'],
- $options['contact_phone'],
- ), ',', '|' );
-
- // Payee Name Record
- fputcsv( $report, array(
- 'PAYENM',
- substr( $payable_to, 0, 35 ),
- '',
- sprintf( '%d-%d', $index->blog_id, $index->post_id ),
- ), ',', '|' );
-
- // Payee Address Record
- fputcsv( $report, array(
- 'PYEADD',
- substr( get_post_meta( $post->ID, '_camppayments_vendor_street_address', true ), 0, 35 ),
- '',
- ), ',', '|' );
-
- // Additional Payee Address Record
- fputcsv( $report, array( 'ADDPYE', '', '' ), ',', '|' );
-
- // Payee Postal Record
- fputcsv( $report, array(
- 'PYEPOS',
- substr( get_post_meta( $post->ID, '_camppayments_vendor_city', true ), 0, 35 ),
- substr( get_post_meta( $post->ID, '_camppayments_vendor_state', true ), 0, 35 ),
- substr( get_post_meta( $post->ID, '_camppayments_vendor_zip_code', true ), 0, 10 ),
- substr( $vendor_country_code, 0, 3 ),
- ), ',', '|' );
-
- // Payment Description
- fputcsv( $report, array(
- 'PYTDES',
- substr( $description, 0, 122 ),
- ), ',', '|' );
-
- restore_current_blog();
- }
-
- // File Trailer
- fputcsv( $report, array( 'FILTRL', $count * 6 + 2 ), ',', '|' );
-
- // Update counter and unlock
- $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
- update_site_option( '_wcb_jpm_checks_counter', $start + $count );
- delete_site_transient( '_wcb_jpm_checks_counter_lock' );
-
- fclose( $report );
- return ob_get_clean();
- }
-
- /**
- * NACHA via JP Morgan
- *
- * @param array $args
- *
- * @return string
- */
- protected static function _generate_payment_report_jpm_ach( $args ) {
- $args = wp_parse_args( $args, array(
- 'request_indexes' => array(),
- 'status' => '',
- ) );
-
- $ach_options = apply_filters( 'wcb_payment_req_ach_options', array(
- 'bank-routing-number' => '', // Immediate Destination (bank routing number)
- 'company-id' => '', // Company ID
- 'financial-inst' => '', // Originating Financial Institution
- ) );
-
- ob_start();
-
- // File Header Record
-
- echo '1'; // Record Type Code
- echo '01'; // Priority Code
- echo ' ' . str_pad( substr( $ach_options['bank-routing-number'], 0, 9 ), 9, '0', STR_PAD_LEFT );
- echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10, '0', STR_PAD_LEFT ); // Immediate Origin (TIN)
- echo date( 'ymd' ); // Transmission Date
- echo date( 'Hi' ); // Transmission Time
- echo 'A'; // File ID Modifier
- echo '094'; // Record Size
- echo '10'; // Blocking Factor
- echo '1'; // Format Code
- echo str_pad( 'JPMORGANCHASE', 23 ); // Destination
- echo str_pad( 'WCEXPORT', 23 ); // Origin
- echo str_pad( '', 8 ); // Reference Code (optional)
- echo PHP_EOL;
-
- // Batch Header Record
-
- echo '5'; // Record Type Code
- echo '200'; // Service Type Code
- echo 'WordCamp Communi'; // Company Name
- echo str_pad( '', 20 ); // Blanks
- echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company Identification
-
- // Get the first one in the set.
- // @todo Split batches by account type.
- foreach ( $args['request_indexes'] as $index ) {
- switch_to_blog( $index->blog_id );
- $post = get_post( $index->post_id );
- $account_type = get_post_meta( $post->ID, '_camppayments_ach_account_type', true );
- restore_current_blog();
-
- break;
- }
-
- $entry_class = $account_type == 'Personal' ? 'PPD' : 'CCD';
- echo $entry_class; // Standard Entry Class
-
- echo 'Vendor Pay'; // Entry Description
- echo date( 'ymd', self::_next_business_day_timestamp() ); // Company Description Date
- echo date( 'ymd', self::_next_business_day_timestamp() ); // Effective Entry Date
- echo str_pad( '', 3 ); // Blanks
- echo '1'; // Originator Status Code
- echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
- echo '0000001'; // Batch Number
- echo PHP_EOL;
-
- $count = 0;
- $total = 0;
- $hash = 0;
-
- foreach ( $args['request_indexes'] as $index ) {
- switch_to_blog( $index->blog_id );
- $post = get_post( $index->post_id );
-
- if ( $args['status'] && $post->post_status != $args['status'] )
- continue;
-
- if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Direct Deposit' )
- continue;
-
- $count++;
-
- // Entry Detail Record
-
- echo '6'; // Record Type Code
-
- $transaction_code = $account_type == 'Personal' ? '27' : '22';
- echo $transaction_code; // Transaction Code
-
- // Transit/Routing Number of Destination Bank + Check digit
- $routing_number = get_post_meta( $post->ID, '_camppayments_ach_routing_number', true );
- $routing_number = WCP_Encryption::maybe_decrypt( $routing_number );
- $routing_number = substr( $routing_number, 0, 8 + 1 );
- $routing_number = str_pad( $routing_number, 8 + 1 );
- $hash += absint( substr( $routing_number, 0, 8 ) );
- echo $routing_number;
-
- // Bank Account Number
- $account_number = get_post_meta( $post->ID, '_camppayments_ach_account_number', true );
- $account_number = WCP_Encryption::maybe_decrypt( $account_number );
- $account_number = substr( $account_number, 0, 17 );
- $account_number = str_pad( $account_number, 17 );
- echo $account_number;
-
- // Amount
- $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
- $total += $amount;
- $amount = str_pad( number_format( $amount, 2, '', '' ), 10, '0', STR_PAD_LEFT );
- echo $amount;
-
- // Individual Identification Number
- echo str_pad( sprintf( '%d-%d', $index->blog_id, $index->post_id ), 15 );
-
- // Individual Name
- $name = get_post_meta( $post->ID, '_camppayments_ach_account_holder_name', true );
- $name = WCP_Encryption::maybe_decrypt( $name );
- $name = substr( $name, 0, 22 );
- $name = str_pad( $name, 22 );
- echo $name;
-
- echo ' '; // User Defined Data
- echo '0'; // Addenda Record Indicator
-
- // Trace Number
- echo str_pad( substr( $ach_options['bank-routing-number'], 0, 8 ), 8, '0', STR_PAD_LEFT ); // routing number
- echo str_pad( $count, 7, '0', STR_PAD_LEFT ); // sequence number
- echo PHP_EOL;
- }
-
- // Batch Trailer Record
-
- echo '8'; // Record Type Code
- echo '200'; // Service Class Code
- echo str_pad( $count, 6, '0', STR_PAD_LEFT ); // Entry/Addenda Count
- echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
- echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
- echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
- echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company ID
- echo str_pad( '', 25 ); // Blanks
- echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
- echo '0000001'; // Batch Number
- echo PHP_EOL;
-
-
- // File Trailer Record
-
- echo '9'; // Record Type Code
- echo '000001'; // Batch Count
- echo str_pad( ceil( $count / 10 ), 6, '0', STR_PAD_LEFT ); // Block Count
- echo str_pad( $count, 8, '0', STR_PAD_LEFT ); // Entry/Addenda Count
- echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
- echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
- echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
- echo str_pad( '', 39 ); // Blanks
- echo PHP_EOL;
-
- // The file must have a number of lines that is a multiple of 10 (e.g. 10, 20, 30).
- echo str_repeat( PHP_EOL, 10 - ( ( 4 + $count ) % 10 ) - 1 );
- return ob_get_clean();
- }
-
- /**
- * Exclude weekends and JPM holidays.
- *
- * Needs to be updated every year.
- *
- * @return int Timestamp.
- */
- private static function _next_business_day_timestamp() {
- static $timestamp;
-
- if ( isset( $timestamp ) )
- return $timestamp;
-
- $holidays = array(
- date( 'Ymd', strtotime( 'Friday, January 1, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, January 18, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, February 15, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, May 30, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, July 4, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, September 5, 2016' ) ),
- date( 'Ymd', strtotime( 'Friday, November 11, 2016' ) ),
- date( 'Ymd', strtotime( 'Thursday, November 24, 2016' ) ),
- date( 'Ymd', strtotime( 'Monday, December 26, 2016' ) ),
- );
-
- $timestamp = strtotime( 'today + 1 weekday' );
- $attempts = 5;
-
- while ( in_array( date( 'Ymd', $timestamp ), $holidays ) ) {
- $timestamp = strtotime( '+ 1 weekday', $timestamp );
- $attempts--;
-
- if ( ! $attempts )
- break;
- }
-
- return $timestamp;
- }
-
- /**
- * Wires via JP Morgan
- *
- * @param array $args
- *
- * @return string
- */
- protected static function _generate_payment_report_jpm_wires( $args ) {
- $args = wp_parse_args( $args, array(
- 'request_indexes' => array(),
- 'status' => '',
- ) );
-
- ob_start();
- $report = fopen( 'php://output', 'w' );
-
- // JPM Header
- fputcsv( $report, array( 'HEADER', gmdate( 'YmdHis' ), '1' ) );
-
- $total = 0;
- $count = 0;
-
- foreach ( $args['request_indexes'] as $index ) {
- switch_to_blog( $index->blog_id );
- $post = get_post( $index->post_id );
-
- if ( $args['status'] && $post->post_status != $args['status'] )
- continue;
-
- // Only wires here.
- if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Wire' )
- continue;
-
- $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2);
- $total += $amount;
- $count += 1;
-
- // If account starts with two letters, it's most likely an IBAN
- $account = get_post_meta( $post->ID, '_camppayments_beneficiary_account_number', true );
- $account = WCP_Encryption::maybe_decrypt( $account );
- $account = preg_replace( '#\s#','', $account );
- $account_type = preg_match( '#^[a-z]{2}#i', $account ) ? 'IBAN' : 'ACCT';
-
- $row = array(
- '1-input-type' => 'P',
- '2-payment-method' => 'WIRES',
- '3-debit-bank-id' => apply_filters( 'wcb_payment_req_bank_id', '' ), // external file
- '4-account-number' => apply_filters( 'wcb_payment_req_bank_number', '' ), // external file
- '5-bank-to-bank' => 'N',
- '6-txn-currency' => get_post_meta( $post->ID, '_camppayments_currency', true ),
- '7-txn-amount' => $amount,
- '8-equiv-amount' => '',
- '9-clearing' => '',
- '10-ben-residence' => '',
- '11-rate-type' => '',
- '12-blank' => '',
- '13-value-date' => '',
-
- '14-id-type' => $account_type,
- '15-id-value' => $account,
- '16-ben-name' => substr( WCP_Encryption::maybe_decrypt(
- get_post_meta( $post->ID, '_camppayments_beneficiary_name', true ) ), 0, 35 ),
- '17-address-1' => substr( WCP_Encryption::maybe_decrypt(
- get_post_meta( $post->ID, '_camppayments_beneficiary_street_address', true ) ), 0, 35 ),
- '18-address-2' => '',
- '19-city-state-zip' => substr( sprintf( '%s %s %s',
- WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_city', true ) ),
- WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_state', true ) ),
- WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_zip_code', true ) )
- ), 0, 32 ),
- '20-blank' => '',
- '21-country' => WCP_Encryption::maybe_decrypt(
- get_post_meta( $post->ID, '_camppayments_beneficiary_country_iso3166', true ) ),
- '22-blank' => '',
- '23-blank' => '',
-
- '24-id-type' => 'SWIFT',
- '25-id-value' => get_post_meta( $post->ID, '_camppayments_bank_bic', true ),
- '26-ben-bank-name' => substr( get_post_meta( $post->ID, '_camppayments_bank_name', true ), 0, 35 ),
- '27-ben-bank-address-1' => substr( get_post_meta( $post->ID, '_camppayments_bank_street_address', true ), 0, 35 ),
- '28-ben-bank-address-2' => '',
- '29-ben-bank-address-3' => substr( sprintf( '%s %s %s',
- get_post_meta( $post->ID, '_camppayments_bank_city', true ),
- get_post_meta( $post->ID, '_camppayments_bank_state', true ),
- get_post_meta( $post->ID, '_camppayments_bank_zip_code', true )
- ), 0, 35 ),
- '30-ben-bank-country' => get_post_meta( $post->ID, '_camppayments_bank_country_iso3166', true ),
- '31-supl-id-type' => '',
- '32-supl-id-value' => '',
-
- '33-blank' => '',
- '34-blank' => '',
- '35-blank' => '',
- '36-blank' => '',
- '37-blank' => '',
- '38-blank' => '',
- '39-blank' => '',
-
- // Filled out later if not empty.
- '40-id-type' => '',
- '41-id-value' => '',
- '42-interm-bank-name' => '',
- '43-interm-bank-address-1' => '',
- '44-interm-bank-address-2' => '',
- '45-interm-bank-address-3' => '',
- '46-interm-bank-country' => '',
- '47-supl-id-type' => '',
- '48-supl-id-value' => '',
-
- '49-id-type' => '',
- '50-id-value' => '',
- '51-party-name' => '',
- '52-party-address-1' => '',
- '53-party-address-2' => '',
- '54-party-address-3' => '',
- '55-party-country' => '',
-
- '56-blank' => '',
- '57-blank' => '',
- '58-blank' => '',
- '59-blank' => '',
- '60-blank' => '',
- '61-blank' => '',
- '62-blank' => '',
- '63-blank' => '',
- '64-blank' => '',
- '65-blank' => '',
- '66-blank' => '',
- '67-blank' => '',
- '68-blank' => '',
- '69-blank' => '',
- '70-blank' => '',
- '71-blank' => '',
- '72-blank' => '',
- '73-blank' => '',
-
- '74-ref-text' => substr( get_post_meta( $post->ID, '_camppayments_invoice_number', true ), 0, 16 ),
- '75-internal-ref' => '',
- '76-on-behalf-of' => '',
-
- '77-detial-1' => '',
- '78-detial-2' => '',
- '79-detial-3' => '',
- '80-detail-4' => '',
-
- '81-blank' => '',
- '82-blank' => '',
- '83-blank' => '',
- '84-blank' => '',
- '85-blank' => '',
- '86-blank' => '',
- '87-blank' => '',
- '88-blank' => '',
-
- '89-reporting-code' => '',
- '90-country' => '',
- '91-inst-1' => '',
- '92-inst-2' => '',
- '93-inst-3' => '',
- '94-inst-code-1' => '',
- '95-inst-text-1' => '',
- '96-inst-code-2' => '',
- '97-inst-text-2' => '',
- '98-inst-code-3' => '',
- '99-inst-text-3' => '',
-
- '100-stor-code-1' => '',
- '101-stor-line-2' => '', // Hmm?
- '102-stor-code-2' => '',
- '103-stor-line-2' => '',
- '104-stor-code-3' => '',
- '105-stor-line-3' => '',
- '106-stor-code-4' => '',
- '107-stor-line-4' => '',
- '108-stor-code-5' => '',
- '109-stor-line-5' => '',
- '110-stor-code-6' => '',
- '111-stor-line-6' => '',
-
- '112-priority' => '',
- '113-blank' => '',
- '114-charges' => '',
- '115-blank' => '',
- '116-details' => '',
- '117-note' => substr( sprintf( 'wcb-%d-%d', $index->blog_id, $index->post_id ), 0, 70 ),
- );
-
- // If an intermediary bank is given.
- $interm_swift = get_post_meta( $post->ID, '_camppayments_interm_bank_swift', true );
- if ( ! empty( $iterm_swift ) ) {
- $row['40-id-type'] = 'SWIFT';
- $row['41-id-value'] = $interm_swift;
-
- $row['42-interm-bank-name'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_name', true ), 0, 35 );
- $row['43-interm-bank-address-1'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_street_address', true ), 0, 35 );
-
- $row['44-interm-bank-address-2'] = '';
- $row['45-interm-bank-address-3'] = substr( sprintf( '%s %s %s',
- get_post_meta( $post->ID, '_camppayments_interm_bank_city', true ),
- get_post_meta( $post->ID, '_camppayments_interm_bank_state', true ),
- get_post_meta( $post->ID, '_camppayments_interm_bank_zip_code', true )
- ), 0, 32 );
-
- $row['46-interm-bank-country'] = get_post_meta( $post->ID, '_camppayments_interm_bank_country_iso3166', true );
-
- $row['47-supl-id-type'] = 'ACCT';
- $row['48-supl-id-value'] = get_post_meta( $post->ID, '_camppayments_interm_bank_account', true );
- }
-
- // Because CSV is stupid:
- // print_r( $row );
-
- fputcsv( $report, array_values( $row ) );
- restore_current_blog();
- }
-
- // JPM Trailer
- fputcsv( $report, array( 'TRAILER', $count, $total ) );
-
- fclose( $report );
- $results = ob_get_clean();
-
- // JPM chokes on accents and non-latin characters.
- $results = remove_accents( $results );
- return $results;
- }
-
- /**
- * Gather all the request details needed for a row in the export file
- *
- * @param stdClass $index
- * @param array $args
- *
- * @return array
- */
- protected static function get_report_row( $index, $args ) {
- switch_to_blog( $index->blog_id );
-
- $request = get_post( $index->post_id );
-
- $back_compat_statuses = array(
- 'unpaid' => 'draft',
- 'incomplete' => 'wcb-incomplete',
- 'paid' => 'wcb-paid',
- );
-
- // Map old statuses to new statuses.
- if ( array_key_exists( $request->post_status, $back_compat_statuses ) ) {
- $request->post_status = $back_compat_statuses[ $request->post_status ];
- }
-
- if ( $args['status'] && $request->post_status != $args['status'] ) {
- return null;
- }
-
- $currency = get_post_meta( $index->post_id, '_camppayments_currency', true );
- $category = get_post_meta( $index->post_id, '_camppayments_payment_category', true );
- $date_vendor_paid = get_post_meta( $index->post_id, '_camppayments_date_vendor_paid', true );
-
- if ( $date_vendor_paid ) {
- $date_vendor_paid = date( 'Y-m-d', $date_vendor_paid );
- }
-
- if ( 'null-select-one' === $currency ) {
- $currency = '';
- }
-
- if ( 'null' === $category ) {
- $category = '';
- }
-
- $row = array(
- get_wordcamp_name(),
- sprintf( '%d-%d', $index->blog_id, $index->post_id ),
- $request->post_title,
- $index->status,
- $date_vendor_paid,
- date( 'Y-m-d', $index->created ),
- date( 'Y-m-d', $index->due ),
- get_post_meta( $index->post_id, '_camppayments_payment_amount', true ),
- $currency,
- $category,
- get_post_meta( $index->post_id, '_camppayments_payment_method', true ),
- get_post_meta( $index->post_id, '_camppayments_vendor_name', true ),
- get_post_meta( $index->post_id, '_camppayments_vendor_contact_person', true ),
- get_post_meta( $index->post_id, '_camppayments_vendor_country', true ),
- WCP_Encryption::maybe_decrypt( get_post_meta( $index->post_id, '_camppayments_payable_to', true ) ),
- get_edit_post_link( $index->post_id ),
- get_post_meta( $index->post_id, '_camppayments_file_notes', true ),
- );
-
- restore_current_blog();
-
- return $row;
- }
-
- /**
- * Render the Export tab
- */
- protected static function render_export_tab() {
- $today = date( 'Y-m-d' );
- $last_month = date( 'Y-m-d', strtotime( 'now - 1 month' ) );
- ?>
-
- <script>
- /**
- * Fallback to the jQueryUI datepicker if the browser doesn't support <input type="date">
- */
- jQuery( document ).ready( function( $ ) {
- var browserTest = document.createElement( 'input' );
- browserTest.setAttribute( 'type', 'date' );
-
- if ( 'text' === browserTest.type ) {
- $( '#wcpn_export' ).find( 'input[type=date]' ).datepicker( {
- dateFormat : 'yy-mm-dd',
- changeMonth: true,
- changeYear : true
- } );
- }
- } );
- </script>
-
- <form id="wcpn_export" method="POST">
- <?php wp_nonce_field( 'export', 'wcpn_request_export' ); ?>
-
- <h2>Export Settings</h2>
-
- <table class="form-table">
- <tr>
- <th><label>Status</label></th>
- <td>
- <select name="wcpn_export_status">
- <option value="wcb-approved"><?php _e( 'Approved', 'wordcamporg' ); ?></option>
- <option value="wcb-paid"><?php _e( 'Paid', 'wordcamporg' ); ?></option>
- </select>
- </td>
- </tr>
- <tr>
- <th><label>Date Range</label></th>
- <td>
- <input type="date" name="wcpn_export_start_date" class="medium-text" value="<?php echo esc_attr( $last_month ); ?>" /> to
- <input type="date" name="wcpn_export_end_date" class="medium-text" value="<?php echo esc_attr( $today ); ?>" />
- </td>
- </tr>
- <tr>
- <th><label>Format</label></th>
- <td>
- <select name="wcpn_export_type">
- <?php foreach ( self::get_export_types() as $key => $export_type ) : ?>
- <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $export_type['label'] ); ?></option>
- <?php endforeach; ?>
- </select>
- </td>
- </tr>
- </table>
-
- <?php submit_button( 'Download Export' ); ?>
- </form>
-
- <?php
- }
-
- /**
</del><span class="cx" style="display: block; padding: 0 10px"> * Renders the import tab.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function render_import_tab() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1397,7 +529,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'cancelled-failed',
</span><span class="cx" style="display: block; padding: 0 10px"> 'incomplete',
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'export',
</del><span class="cx" style="display: block; padding: 0 10px"> 'import',
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1422,7 +553,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'paid' => __( 'Paid', 'wordcamporg' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 'cancelled-failed' => __( 'Cancelled/Failed', 'wordcamporg' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 'incomplete' => __( 'Incomplete', 'wordcamporg' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'export' => __( 'Export', 'wordcamporg' ),
</del><span class="cx" style="display: block; padding: 0 10px"> 'import' => __( 'Import', 'wordcamporg' ),
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px">
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsnetworkincludeswordcampbudgetsdashboardphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/wordcamp-budgets-dashboard.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/wordcamp-budgets-dashboard.php 2016-03-03 23:05:06 UTC (rev 2693)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments-network/includes/wordcamp-budgets-dashboard.php 2016-03-04 10:34:58 UTC (rev 2694)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7,10 +7,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * Core functionality and helper functions shared between modules
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-add_action( 'network_admin_menu', __NAMESPACE__ . '\register_budgets_menu' );
-add_action( 'network_admin_menu', __NAMESPACE__ . '\remove_budgets_submenu', 11 ); // after other modules have registered their submenu pages
-add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'network_admin_menu', __NAMESPACE__ . '\register_budgets_menu' );
+add_action( 'network_admin_menu', __NAMESPACE__ . '\remove_budgets_submenu', 11 ); // after other modules have registered their submenu pages
+add_action( 'network_admin_menu', __NAMESPACE__ . '\import_export_admin_menu', 11 );
+add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts', 10, 1 );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'admin_init', __NAMESPACE__ . '\process_export_request' );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Register the Budgets Dashboard menu
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -31,24 +34,960 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Register the Import/Export dashboard menu item.
+ */
+function import_export_admin_menu() {
+ add_submenu_page(
+ 'wordcamp-budgets-dashboard',
+ 'WordCamp Budgets Import/Export',
+ 'Import/Export',
+ 'manage_network',
+ 'wcb-import-export',
+ __NAMESPACE__ . '\render_import_export'
+ );
+}
+
+/**
+ * Render the import/export screen.
+ */
+function render_import_export() {
+ $current_tab = 'import';
+ if ( ! empty( $_GET['section'] ) && in_array( $_GET['section'], array( 'import', 'export' ) ) ) {
+ $current_tab = $_GET['section'];
+ }
+
+ ?>
+ <div class="wrap">
+ <h1>Import/Export</h1>
+
+ <?php do_action( 'admin_notices' ); ?>
+ <?php settings_errors(); ?>
+
+ <h3 class="nav-tab-wrapper">
+ <a class="nav-tab <?php if ( $current_tab == 'import' ) { echo 'nav-tab-active'; } ?>"
+ href="<?php echo add_query_arg( array(
+ 'page' => 'wcb-import-export',
+ 'section' => 'import',
+ ), network_admin_url( 'admin.php' ) ); ?>">Import</a>
+
+ <a class="nav-tab <?php if ( $current_tab == 'export' ) { echo 'nav-tab-active'; } ?>"
+ href="<?php echo add_query_arg( array(
+ 'page' => 'wcb-import-export',
+ 'section' => 'export',
+ ), network_admin_url( 'admin.php' ) ); ?>">Export</a>
+ </h3>
+
+ <?php
+ if ( 'export' == $current_tab ) {
+ render_export_tab();
+ } elseif ( 'import' == $current_tab ) {
+ render_import_tab();
+ }
+ ?>
+
+ </div> <!-- /wrap -->
+ <?php
+}
+
+/**
+ * Get available export options.
+ *
+ * @return array
+ */
+function get_export_types() {
+ return array(
+ 'default' => array(
+ 'label' => 'Regular CSV',
+ 'mime_type' => 'text/csv',
+ 'callback' => __NAMESPACE__ . '\_generate_payment_report_default',
+ 'filename' => 'wordcamp-payments-%s-%s-default.csv',
+ ),
+ 'jpm_wires' => array(
+ 'label' => 'JP Morgan Access - Wire Payments',
+ 'mime_type' => 'text/csv',
+ 'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_wires',
+ 'filename' => 'wordcamp-payments-%s-%s-jpm-wires.csv',
+ ),
+ 'jpm_ach' => array(
+ 'label' => 'JP Morgan - NACHA',
+ 'mime_type' => 'text/plain',
+ 'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_ach',
+ 'filename' => 'wordcamp-payments-%s-%s-jpm-ach.ach',
+ ),
+ 'jpm_checks' => array(
+ 'label' => 'JP Morgan - Quick Checks',
+ 'mime_type' => 'text/csv',
+ 'callback' => __NAMESPACE__ . '\_generate_payment_report_jpm_checks',
+ 'filename' => 'wordcamp-payments-%s-%s-jpm-checks.csv',
+ ),
+ );
+}
+
+/**
+ * Render the Import tab
+ */
+function render_import_tab() {
+ echo '<p>Move along, nothing to see here.</p>';
+}
+
+/**
+ * Render the export tab
+ */
+function render_export_tab() {
+ $today = date( 'Y-m-d' );
+ $last_month = date( 'Y-m-d', strtotime( 'now - 1 month' ) );
+ ?>
+ <script>
+ /**
+ * Fallback to the jQueryUI datepicker if the browser doesn't support <input type="date">
+ */
+ jQuery( document ).ready( function( $ ) {
+ var browserTest = document.createElement( 'input' );
+ browserTest.setAttribute( 'type', 'date' );
+
+ if ( 'text' === browserTest.type ) {
+ $( '#wcpn_export' ).find( 'input[type=date]' ).datepicker( {
+ dateFormat : 'yy-mm-dd',
+ changeMonth: true,
+ changeYear : true
+ } );
+ }
+ } );
+ </script>
+
+ <form id="wcpn_export" method="POST">
+ <?php wp_nonce_field( 'export', 'wcb-request-export' ); ?>
+
+ <h2>Export Settings</h2>
+
+ <table class="form-table">
+ <tr>
+ <th>Types</th>
+ <td>
+ <label><input type="checkbox" name="wcb-export-types-vendor-payments"
+ value="1" checked disabled /> Vendor Payments</label><br />
+ <label><input type="checkbox" name="wcb-export-types-reimbursements"
+ value="1" disabled /> Reimbursements</label>
+ </td>
+ <tr>
+ <th>Status</th>
+ <td>
+ <select name="wcb-export-status">
+ <option value="wcb-approved"><?php _e( 'Approved', 'wordcamporg' ); ?></option>
+ <option value="wcb-paid"><?php _e( 'Paid', 'wordcamporg' ); ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th>Date Range</th>
+ <td>
+ <input type="date" name="wcb-export-start-date"
+ class="medium-text" value="<?php echo esc_attr( $last_month ); ?>" /> to
+ <input type="date" name="wcb-export-end-date"
+ class="medium-text" value="<?php echo esc_attr( $today ); ?>" />
+ </td>
+ </tr>
+ <tr>
+ <th>Format</th>
+ <td>
+ <select name="wcb-export-type">
+ <?php foreach ( get_export_types() as $key => $export_type ) : ?>
+ <option value="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $export_type['label'] ); ?></option>
+ <?php endforeach; ?>
+ </select>
+ </td>
+ </tr>
+ </table>
+
+ <?php submit_button( 'Download Export' ); ?>
+ </form>
+ <?php
+}
+
+/**
+ * Process export requests
+ */
+function process_export_request() {
+ if ( empty( $_GET['page'] ) || $_GET['page'] != 'wcb-import-export' )
+ return;
+
+ if ( empty( $_GET['section'] ) || $_GET['section'] != 'export' )
+ return;
+
+ if ( empty( $_POST['wcb-request-export'] ) )
+ return;
+
+ if ( ! current_user_can( 'manage_network' ) || ! check_admin_referer( 'export', 'wcb-request-export' ) )
+ return;
+
+ $export_types = get_export_types();
+
+ if ( array_key_exists( $_POST['wcb-export-type'], $export_types ) ) {
+ $export_type = $export_types[ $_POST['wcb-export-type'] ];
+ } else {
+ $export_type = $export_types['default'];
+ }
+
+ $status = $_POST['wcb-export-status'];
+ if ( ! in_array( $status, array( 'wcb-approved', 'wcb-paid' ) ) )
+ $status = 'wcb-approved';
+
+ $start_date = strtotime( $_POST['wcb-export-start-date'] . ' 00:00:00' );
+ $end_date = strtotime( $_POST['wcb-export-end-date'] . ' 23:59:59' );
+ $filename = sprintf( $export_type['filename'], date( 'Ymd', $start_date ), date( 'Ymd', $end_date ) );
+ $filename = sanitize_file_name( $filename );
+
+ $report = generate_payment_report( array(
+ 'status' => $status,
+ 'start_date' => $start_date,
+ 'end_date' => $end_date,
+ 'export_type' => $export_type,
+ ) );
+
+ if ( is_wp_error( $report ) ) {
+ add_settings_error( 'wcb-dashboard', $report->get_error_code(), $report->get_error_message() );
+ } else {
+ header( sprintf( 'Content-Type: %s', $export_type['mime_type'] ) );
+ header( sprintf( 'Content-Disposition: attachment; filename="%s"', $filename ) );
+ header( 'Cache-control: private' );
+ header( 'Pragma: private' );
+ header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
+
+ echo $report;
+ die();
+ }
+}
+
+/*
+ * Generate and return the raw payment report contents
+ *
+ * @param array $args
+ *
+ * @return string | WP_Error
+ */
+function generate_payment_report( $args ) {
+ global $wpdb;
+
+ $args = wp_parse_args( $args, array(
+ 'status' => '',
+ 'start_date' => '',
+ 'end_date' => '',
+ 'export_type' => '',
+ ) );
+
+ if ( ! is_int( $args['start_date'] ) || ! is_int( $args['end_date'] ) ) {
+ return new WP_Error( 'wcb-bad-dates', 'Invalid start or end date.' );
+ }
+
+ // todo: support other index tables.
+ $table_name = $wpdb->get_blog_prefix(0) . 'wordcamp_payments_index';
+ $date_type = 'updated';
+
+ if ( $args['status'] == 'wcb-paid' )
+ $date_type = 'paid';
+
+ $request_indexes = $wpdb->get_results( $wpdb->prepare( "
+ SELECT *
+ FROM `{$table_name}`
+ WHERE `{$date_type}` BETWEEN %d AND %d",
+ $args['start_date'],
+ $args['end_date']
+ ) );
+
+ if ( ! is_callable( $args['export_type']['callback'] ) )
+ return new \WP_Error( 'wcb-invalid-type', 'The export type is invalid.' );
+
+ $args['request_indexes'] = $request_indexes;
+
+ return call_user_func( $args['export_type']['callback'], $args );
+}
+
+/**
+ * Default CSV report
+ *
+ * @param array $args
+ *
+ * @return string
+ */
+function _generate_payment_report_default( $args ) {
+ $args = wp_parse_args( $args, array(
+ 'request_indexes' => array(),
+ 'status' => '',
+ ) );
+
+ $column_headings = array(
+ 'WordCamp', 'ID', 'Title', 'Status', 'Date Vendor was Paid', 'Creation Date', 'Due Date', 'Amount',
+ 'Currency', 'Category', 'Payment Method','Vendor Name', 'Vendor Contact Person', 'Vendor Country',
+ 'Check Payable To', 'URL', 'Supporting Documentation Notes',
+ );
+
+ ob_start();
+ $report = fopen( 'php://output', 'w' );
+
+ fputcsv( $report, $column_headings );
+
+ foreach( $args['request_indexes'] as $index ) {
+ switch_to_blog( $index->blog_id );
+
+ $request = get_post( $index->post_id );
+
+ $back_compat_statuses = array(
+ 'unpaid' => 'draft',
+ 'incomplete' => 'wcb-incomplete',
+ 'paid' => 'wcb-paid',
+ );
+
+ // Map old statuses to new statuses.
+ if ( array_key_exists( $request->post_status, $back_compat_statuses ) ) {
+ $request->post_status = $back_compat_statuses[ $request->post_status ];
+ }
+
+ if ( $args['status'] && $request->post_status != $args['status'] ) {
+ restore_current_blog();
+ continue;
+ }
+
+ $currency = get_post_meta( $index->post_id, '_camppayments_currency', true );
+ $category = get_post_meta( $index->post_id, '_camppayments_payment_category', true );
+ $date_vendor_paid = get_post_meta( $index->post_id, '_camppayments_date_vendor_paid', true );
+
+ if ( $date_vendor_paid ) {
+ $date_vendor_paid = date( 'Y-m-d', $date_vendor_paid );
+ }
+
+ if ( 'null-select-one' === $currency ) {
+ $currency = '';
+ }
+
+ if ( 'null' === $category ) {
+ $category = '';
+ }
+
+ $row = array(
+ get_wordcamp_name(),
+ sprintf( '%d-%d', $index->blog_id, $index->post_id ),
+ html_entity_decode( $request->post_title ),
+ $index->status,
+ $date_vendor_paid,
+ date( 'Y-m-d', $index->created ),
+ date( 'Y-m-d', $index->due ),
+ get_post_meta( $index->post_id, '_camppayments_payment_amount', true ),
+ $currency,
+ $category,
+ get_post_meta( $index->post_id, '_camppayments_payment_method', true ),
+ get_post_meta( $index->post_id, '_camppayments_vendor_name', true ),
+ get_post_meta( $index->post_id, '_camppayments_vendor_contact_person', true ),
+ get_post_meta( $index->post_id, '_camppayments_vendor_country', true ),
+ \WCP_Encryption::maybe_decrypt( get_post_meta( $index->post_id, '_camppayments_payable_to', true ) ),
+ get_edit_post_link( $index->post_id ),
+ get_post_meta( $index->post_id, '_camppayments_file_notes', true ),
+ );
+
+ restore_current_blog();
+
+ if ( ! empty( $row ) ) {
+ fputcsv( $report, $row );
+ }
+ }
+
+ fclose( $report );
+ return ob_get_clean();
+}
+
+/**
+ * Quick Checks via JP Morgan
+ *
+ * @param array $args
+ *
+ * @return string
+ */
+function _generate_payment_report_jpm_checks( $args ) {
+ $args = wp_parse_args( $args, array(
+ 'request_indexes' => array(),
+ 'status' => '',
+ ) );
+
+ $options = apply_filters( 'wcb_payment_req_check_options', array(
+ 'pws_customer_id' => '',
+ 'account_number' => '',
+ 'contact_email' => '',
+ 'contact_phone' => '',
+ ) );
+
+ $report = fopen( 'php://output', 'w' );
+ ob_start();
+
+ // File Header
+ fputcsv( $report, array( 'FILHDR', 'PWS', $options['pws_customer_id'], date( 'm/d/Y' ), date( 'Hi' ) ), ',', '|' );
+
+ $total = 0;
+ $count = 0;
+
+ if ( false !== get_site_transient( '_wcb_jpm_checks_counter_lock' ) ) {
+ wp_die( 'JPM Checks Export is locked. Please try again later or contact support.' );
+ }
+
+ // Avoid at least *some* race conditions.
+ set_site_transient( '_wcb_jpm_checks_counter_lock', 1, 30 );
+ $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
+
+ foreach ( $args['request_indexes'] as $index ) {
+ switch_to_blog( $index->blog_id );
+ $post = get_post( $index->post_id );
+
+ if ( $args['status'] && $post->post_status != $args['status'] ) {
+ restore_current_blog();
+ continue;
+ }
+
+ if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Check' ) {
+ restore_current_blog();
+ continue;
+ }
+
+ $count++;
+ $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
+ $total += $amount;
+
+ $payable_to = \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_payable_to', true ) );
+ $payable_to = html_entity_decode( $payable_to ); // J&J to J&J
+ $countries = \WordCamp_Budgets::get_valid_countries_iso3166();
+ $vendor_country_code = get_post_meta( $post->ID, '_camppayments_vendor_country_iso3166', true );
+ if ( ! empty( $countries[ $vendor_country_code ] ) ) {
+ $vendor_country_code = $countries[ $vendor_country_code ]['alpha3'];
+ }
+
+ $description = sanitize_text_field( get_post_meta( $post->ID, '_camppayments_description', true ) );
+ $description = html_entity_decode( $description );
+ $invoice_number = get_post_meta( $post->ID, '_camppayments_invoice_number', true );
+ if ( ! empty( $invoice_number ) ) {
+ $description = sprintf( 'Invoice %s. %s', $invoice_number, $description );
+ }
+
+ // Payment Header
+ fputcsv( $report, array(
+ 'PMTHDR',
+ 'USPS',
+ 'QKCHECKS',
+ date( 'm/d/Y' ),
+ number_format( $amount, 2, '.', '' ),
+ $options['account_number'],
+ $start + $count, // must be globally unique?
+ $options['contact_email'],
+ $options['contact_phone'],
+ ), ',', '|' );
+
+ // Payee Name Record
+ fputcsv( $report, array(
+ 'PAYENM',
+ substr( $payable_to, 0, 35 ),
+ '',
+ sprintf( '%d-%d', $index->blog_id, $index->post_id ),
+ ), ',', '|' );
+
+ // Payee Address Record
+ fputcsv( $report, array(
+ 'PYEADD',
+ substr( get_post_meta( $post->ID, '_camppayments_vendor_street_address', true ), 0, 35 ),
+ '',
+ ), ',', '|' );
+
+ // Additional Payee Address Record
+ fputcsv( $report, array( 'ADDPYE', '', '' ), ',', '|' );
+
+ // Payee Postal Record
+ fputcsv( $report, array(
+ 'PYEPOS',
+ substr( get_post_meta( $post->ID, '_camppayments_vendor_city', true ), 0, 35 ),
+ substr( get_post_meta( $post->ID, '_camppayments_vendor_state', true ), 0, 35 ),
+ substr( get_post_meta( $post->ID, '_camppayments_vendor_zip_code', true ), 0, 10 ),
+ substr( $vendor_country_code, 0, 3 ),
+ ), ',', '|' );
+
+ // Payment Description
+ fputcsv( $report, array(
+ 'PYTDES',
+ substr( $description, 0, 122 ),
+ ), ',', '|' );
+
+ restore_current_blog();
+ }
+
+ // File Trailer
+ fputcsv( $report, array( 'FILTRL', $count * 6 + 2 ), ',', '|' );
+
+ // Update counter and unlock
+ $start = absint( get_site_option( '_wcb_jpm_checks_counter', 0 ) );
+ update_site_option( '_wcb_jpm_checks_counter', $start + $count );
+ delete_site_transient( '_wcb_jpm_checks_counter_lock' );
+
+ fclose( $report );
+ return ob_get_clean();
+}
+
+/**
+ * NACHA via JP Morgan
+ *
+ * @param array $args
+ *
+ * @return string
+ */
+function _generate_payment_report_jpm_ach( $args ) {
+ $args = wp_parse_args( $args, array(
+ 'request_indexes' => array(),
+ 'status' => '',
+ ) );
+
+ $ach_options = apply_filters( 'wcb_payment_req_ach_options', array(
+ 'bank-routing-number' => '', // Immediate Destination (bank routing number)
+ 'company-id' => '', // Company ID
+ 'financial-inst' => '', // Originating Financial Institution
+ ) );
+
+ ob_start();
+
+ // File Header Record
+
+ echo '1'; // Record Type Code
+ echo '01'; // Priority Code
+ echo ' ' . str_pad( substr( $ach_options['bank-routing-number'], 0, 9 ), 9, '0', STR_PAD_LEFT );
+ echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10, '0', STR_PAD_LEFT ); // Immediate Origin (TIN)
+ echo date( 'ymd' ); // Transmission Date
+ echo date( 'Hi' ); // Transmission Time
+ echo 'A'; // File ID Modifier
+ echo '094'; // Record Size
+ echo '10'; // Blocking Factor
+ echo '1'; // Format Code
+ echo str_pad( 'JPMORGANCHASE', 23 ); // Destination
+ echo str_pad( 'WCEXPORT', 23 ); // Origin
+ echo str_pad( '', 8 ); // Reference Code (optional)
+ echo PHP_EOL;
+
+ // Batch Header Record
+
+ echo '5'; // Record Type Code
+ echo '200'; // Service Type Code
+ echo 'WordCamp Communi'; // Company Name
+ echo str_pad( '', 20 ); // Blanks
+ echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company Identification
+
+ // Get the first one in the set.
+ // @todo Split batches by account type.
+ foreach ( $args['request_indexes'] as $index ) {
+ switch_to_blog( $index->blog_id );
+ $post = get_post( $index->post_id );
+ $account_type = get_post_meta( $post->ID, '_camppayments_ach_account_type', true );
+ restore_current_blog();
+
+ break;
+ }
+
+ $entry_class = $account_type == 'Personal' ? 'PPD' : 'CCD';
+ echo $entry_class; // Standard Entry Class
+
+ echo 'Vendor Pay'; // Entry Description
+ echo date( 'ymd', _next_business_day_timestamp() ); // Company Description Date
+ echo date( 'ymd', _next_business_day_timestamp() ); // Effective Entry Date
+ echo str_pad( '', 3 ); // Blanks
+ echo '1'; // Originator Status Code
+ echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
+ echo '0000001'; // Batch Number
+ echo PHP_EOL;
+
+ $count = 0;
+ $total = 0;
+ $hash = 0;
+
+ foreach ( $args['request_indexes'] as $index ) {
+ switch_to_blog( $index->blog_id );
+ $post = get_post( $index->post_id );
+
+ if ( $args['status'] && $post->post_status != $args['status'] ) {
+ restore_current_blog();
+ continue;
+ }
+
+ if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Direct Deposit' ) {
+ restore_current_blog();
+ continue;
+ }
+
+ $count++;
+
+ // Entry Detail Record
+
+ echo '6'; // Record Type Code
+
+ $transaction_code = $account_type == 'Personal' ? '27' : '22';
+ echo $transaction_code; // Transaction Code
+
+ // Transit/Routing Number of Destination Bank + Check digit
+ $routing_number = get_post_meta( $post->ID, '_camppayments_ach_routing_number', true );
+ $routing_number = \WCP_Encryption::maybe_decrypt( $routing_number );
+ $routing_number = substr( $routing_number, 0, 8 + 1 );
+ $routing_number = str_pad( $routing_number, 8 + 1 );
+ $hash += absint( substr( $routing_number, 0, 8 ) );
+ echo $routing_number;
+
+ // Bank Account Number
+ $account_number = get_post_meta( $post->ID, '_camppayments_ach_account_number', true );
+ $account_number = \WCP_Encryption::maybe_decrypt( $account_number );
+ $account_number = substr( $account_number, 0, 17 );
+ $account_number = str_pad( $account_number, 17 );
+ echo $account_number;
+
+ // Amount
+ $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2 );
+ $total += $amount;
+ $amount = str_pad( number_format( $amount, 2, '', '' ), 10, '0', STR_PAD_LEFT );
+ echo $amount;
+
+ // Individual Identification Number
+ echo str_pad( sprintf( '%d-%d', $index->blog_id, $index->post_id ), 15 );
+
+ // Individual Name
+ $name = get_post_meta( $post->ID, '_camppayments_ach_account_holder_name', true );
+ $name = \WCP_Encryption::maybe_decrypt( $name );
+ $name = substr( $name, 0, 22 );
+ $name = str_pad( $name, 22 );
+ echo $name;
+
+ echo ' '; // User Defined Data
+ echo '0'; // Addenda Record Indicator
+
+ // Trace Number
+ echo str_pad( substr( $ach_options['bank-routing-number'], 0, 8 ), 8, '0', STR_PAD_LEFT ); // routing number
+ echo str_pad( $count, 7, '0', STR_PAD_LEFT ); // sequence number
+ echo PHP_EOL;
+ }
+
+ // Batch Trailer Record
+
+ echo '8'; // Record Type Code
+ echo '200'; // Service Class Code
+ echo str_pad( $count, 6, '0', STR_PAD_LEFT ); // Entry/Addenda Count
+ echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
+ echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
+ echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
+ echo str_pad( substr( $ach_options['company-id'], 0, 10 ), 10 ); // Company ID
+ echo str_pad( '', 25 ); // Blanks
+ echo str_pad( substr( $ach_options['financial-inst'], 0, 8 ), 8 ); // Originating Financial Institution
+ echo '0000001'; // Batch Number
+ echo PHP_EOL;
+
+
+ // File Trailer Record
+
+ echo '9'; // Record Type Code
+ echo '000001'; // Batch Count
+ echo str_pad( ceil( $count / 10 ), 6, '0', STR_PAD_LEFT ); // Block Count
+ echo str_pad( $count, 8, '0', STR_PAD_LEFT ); // Entry/Addenda Count
+ echo str_pad( substr( $hash, -10 ), 10, '0', STR_PAD_LEFT ); // Entry Hash
+ echo str_pad( number_format( $total, 2, '', '' ), 12, '0', STR_PAD_LEFT ); // Total Debit Entry Dollar Amount
+ echo str_pad( 0, 12, '0', STR_PAD_LEFT ); // Total Credit Entry Dollar Amount
+ echo str_pad( '', 39 ); // Blanks
+ echo PHP_EOL;
+
+ // The file must have a number of lines that is a multiple of 10 (e.g. 10, 20, 30).
+ echo str_repeat( PHP_EOL, 10 - ( ( 4 + $count ) % 10 ) - 1 );
+ return ob_get_clean();
+}
+
+/**
+ * Wires via JP Morgan
+ *
+ * @param array $args
+ *
+ * @return string
+ */
+function _generate_payment_report_jpm_wires( $args ) {
+ $args = wp_parse_args( $args, array(
+ 'request_indexes' => array(),
+ 'status' => '',
+ ) );
+
+ ob_start();
+ $report = fopen( 'php://output', 'w' );
+
+ // JPM Header
+ fputcsv( $report, array( 'HEADER', gmdate( 'YmdHis' ), '1' ) );
+
+ $total = 0;
+ $count = 0;
+
+ foreach ( $args['request_indexes'] as $index ) {
+ switch_to_blog( $index->blog_id );
+ $post = get_post( $index->post_id );
+
+ if ( $args['status'] && $post->post_status != $args['status'] ) {
+ restore_current_blog();
+ continue;
+ }
+
+ // Only wires here.
+ if ( get_post_meta( $post->ID, '_camppayments_payment_method', true ) != 'Wire' ) {
+ restore_current_blog();
+ continue;
+ }
+
+ $amount = round( floatval( get_post_meta( $post->ID, '_camppayments_payment_amount', true ) ), 2);
+ $total += $amount;
+ $count += 1;
+
+ // If account starts with two letters, it's most likely an IBAN
+ $account = get_post_meta( $post->ID, '_camppayments_beneficiary_account_number', true );
+ $account = \WCP_Encryption::maybe_decrypt( $account );
+ $account = preg_replace( '#\s#','', $account );
+ $account_type = preg_match( '#^[a-z]{2}#i', $account ) ? 'IBAN' : 'ACCT';
+
+ $row = array(
+ '1-input-type' => 'P',
+ '2-payment-method' => 'WIRES',
+ '3-debit-bank-id' => apply_filters( 'wcb_payment_req_bank_id', '' ), // external file
+ '4-account-number' => apply_filters( 'wcb_payment_req_bank_number', '' ), // external file
+ '5-bank-to-bank' => 'N',
+ '6-txn-currency' => get_post_meta( $post->ID, '_camppayments_currency', true ),
+ '7-txn-amount' => number_format( $amount, 2, '.', '' ),
+ '8-equiv-amount' => '',
+ '9-clearing' => '',
+ '10-ben-residence' => '',
+ '11-rate-type' => '',
+ '12-blank' => '',
+ '13-value-date' => '',
+
+ '14-id-type' => $account_type,
+ '15-id-value' => $account,
+ '16-ben-name' => substr( \WCP_Encryption::maybe_decrypt(
+ get_post_meta( $post->ID, '_camppayments_beneficiary_name', true ) ), 0, 35 ),
+ '17-address-1' => substr( \WCP_Encryption::maybe_decrypt(
+ get_post_meta( $post->ID, '_camppayments_beneficiary_street_address', true ) ), 0, 35 ),
+ '18-address-2' => '',
+ '19-city-state-zip' => substr( sprintf( '%s %s %s',
+ \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_city', true ) ),
+ \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_state', true ) ),
+ \WCP_Encryption::maybe_decrypt( get_post_meta( $post->ID, '_camppayments_beneficiary_zip_code', true ) )
+ ), 0, 32 ),
+ '20-blank' => '',
+ '21-country' => \WCP_Encryption::maybe_decrypt(
+ get_post_meta( $post->ID, '_camppayments_beneficiary_country_iso3166', true ) ),
+ '22-blank' => '',
+ '23-blank' => '',
+
+ '24-id-type' => 'SWIFT',
+ '25-id-value' => get_post_meta( $post->ID, '_camppayments_bank_bic', true ),
+ '26-ben-bank-name' => substr( get_post_meta( $post->ID, '_camppayments_bank_name', true ), 0, 35 ),
+ '27-ben-bank-address-1' => substr( get_post_meta( $post->ID, '_camppayments_bank_street_address', true ), 0, 35 ),
+ '28-ben-bank-address-2' => '',
+ '29-ben-bank-address-3' => substr( sprintf( '%s %s %s',
+ get_post_meta( $post->ID, '_camppayments_bank_city', true ),
+ get_post_meta( $post->ID, '_camppayments_bank_state', true ),
+ get_post_meta( $post->ID, '_camppayments_bank_zip_code', true )
+ ), 0, 35 ),
+ '30-ben-bank-country' => get_post_meta( $post->ID, '_camppayments_bank_country_iso3166', true ),
+ '31-supl-id-type' => '',
+ '32-supl-id-value' => '',
+
+ '33-blank' => '',
+ '34-blank' => '',
+ '35-blank' => '',
+ '36-blank' => '',
+ '37-blank' => '',
+ '38-blank' => '',
+ '39-blank' => '',
+
+ // Filled out later if not empty.
+ '40-id-type' => '',
+ '41-id-value' => '',
+ '42-interm-bank-name' => '',
+ '43-interm-bank-address-1' => '',
+ '44-interm-bank-address-2' => '',
+ '45-interm-bank-address-3' => '',
+ '46-interm-bank-country' => '',
+ '47-supl-id-type' => '',
+ '48-supl-id-value' => '',
+
+ '49-id-type' => '',
+ '50-id-value' => '',
+ '51-party-name' => '',
+ '52-party-address-1' => '',
+ '53-party-address-2' => '',
+ '54-party-address-3' => '',
+ '55-party-country' => '',
+
+ '56-blank' => '',
+ '57-blank' => '',
+ '58-blank' => '',
+ '59-blank' => '',
+ '60-blank' => '',
+ '61-blank' => '',
+ '62-blank' => '',
+ '63-blank' => '',
+ '64-blank' => '',
+ '65-blank' => '',
+ '66-blank' => '',
+ '67-blank' => '',
+ '68-blank' => '',
+ '69-blank' => '',
+ '70-blank' => '',
+ '71-blank' => '',
+ '72-blank' => '',
+ '73-blank' => '',
+
+ '74-ref-text' => substr( get_post_meta( $post->ID, '_camppayments_invoice_number', true ), 0, 16 ),
+ '75-internal-ref' => '',
+ '76-on-behalf-of' => '',
+
+ '77-detial-1' => '',
+ '78-detial-2' => '',
+ '79-detial-3' => '',
+ '80-detail-4' => '',
+
+ '81-blank' => '',
+ '82-blank' => '',
+ '83-blank' => '',
+ '84-blank' => '',
+ '85-blank' => '',
+ '86-blank' => '',
+ '87-blank' => '',
+ '88-blank' => '',
+
+ '89-reporting-code' => '',
+ '90-country' => '',
+ '91-inst-1' => '',
+ '92-inst-2' => '',
+ '93-inst-3' => '',
+ '94-inst-code-1' => '',
+ '95-inst-text-1' => '',
+ '96-inst-code-2' => '',
+ '97-inst-text-2' => '',
+ '98-inst-code-3' => '',
+ '99-inst-text-3' => '',
+
+ '100-stor-code-1' => '',
+ '101-stor-line-2' => '', // Hmm?
+ '102-stor-code-2' => '',
+ '103-stor-line-2' => '',
+ '104-stor-code-3' => '',
+ '105-stor-line-3' => '',
+ '106-stor-code-4' => '',
+ '107-stor-line-4' => '',
+ '108-stor-code-5' => '',
+ '109-stor-line-5' => '',
+ '110-stor-code-6' => '',
+ '111-stor-line-6' => '',
+
+ '112-priority' => '',
+ '113-blank' => '',
+ '114-charges' => '',
+ '115-blank' => '',
+ '116-details' => '',
+ '117-note' => substr( sprintf( 'wcb-%d-%d', $index->blog_id, $index->post_id ), 0, 70 ),
+ );
+
+ // If an intermediary bank is given.
+ $interm_swift = get_post_meta( $post->ID, '_camppayments_interm_bank_swift', true );
+ if ( ! empty( $iterm_swift ) ) {
+ $row['40-id-type'] = 'SWIFT';
+ $row['41-id-value'] = $interm_swift;
+
+ $row['42-interm-bank-name'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_name', true ), 0, 35 );
+ $row['43-interm-bank-address-1'] = substr( get_post_meta( $post->ID, '_camppayments_interm_bank_street_address', true ), 0, 35 );
+
+ $row['44-interm-bank-address-2'] = '';
+ $row['45-interm-bank-address-3'] = substr( sprintf( '%s %s %s',
+ get_post_meta( $post->ID, '_camppayments_interm_bank_city', true ),
+ get_post_meta( $post->ID, '_camppayments_interm_bank_state', true ),
+ get_post_meta( $post->ID, '_camppayments_interm_bank_zip_code', true )
+ ), 0, 32 );
+
+ $row['46-interm-bank-country'] = get_post_meta( $post->ID, '_camppayments_interm_bank_country_iso3166', true );
+
+ $row['47-supl-id-type'] = 'ACCT';
+ $row['48-supl-id-value'] = get_post_meta( $post->ID, '_camppayments_interm_bank_account', true );
+ }
+
+ // Use for debugging.
+ // print_r( $row );
+
+ fputcsv( $report, array_values( $row ) );
+ restore_current_blog();
+ }
+
+ // JPM Trailer
+ fputcsv( $report, array( 'TRAILER', $count, $total ) );
+
+ fclose( $report );
+ $results = ob_get_clean();
+
+ // JPM chokes on accents and non-latin characters.
+ $results = remove_accents( $results );
+ return $results;
+}
+
+/**
+ * Exclude weekends and JPM holidays.
+ *
+ * Needs to be updated every year.
+ *
+ * @return int Timestamp.
+ */
+function _next_business_day_timestamp() {
+ static $timestamp;
+
+ if ( isset( $timestamp ) )
+ return $timestamp;
+
+ $holidays = array(
+ date( 'Ymd', strtotime( 'Friday, January 1, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, January 18, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, February 15, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, May 30, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, July 4, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, September 5, 2016' ) ),
+ date( 'Ymd', strtotime( 'Friday, November 11, 2016' ) ),
+ date( 'Ymd', strtotime( 'Thursday, November 24, 2016' ) ),
+ date( 'Ymd', strtotime( 'Monday, December 26, 2016' ) ),
+ );
+
+ $timestamp = strtotime( 'today + 1 weekday' );
+ $attempts = 5;
+
+ while ( in_array( date( 'Ymd', $timestamp ), $holidays ) ) {
+ $timestamp = strtotime( '+ 1 weekday', $timestamp );
+ $attempts--;
+
+ if ( ! $attempts )
+ break;
+ }
+
+ return $timestamp;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Remove the empty Budgets submenu item
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @todo This may no longer be needed once the Budgets post type and Overview pages are added
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> function remove_budgets_submenu() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- remove_submenu_page( 'wordcamp-budgets-dashboard', 'wordcamp-budgets-dashboard' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ remove_submenu_page( 'wordcamp-budgets-dashboard', 'wordcamp-budgets-dashboard' );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Enqueue scripts and styles
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function enqueue_scripts() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function enqueue_scripts( $hook ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_style(
</span><span class="cx" style="display: block; padding: 0 10px"> 'wordcamp-budgets-dashboard',
</span><span class="cx" style="display: block; padding: 0 10px"> plugins_url( 'css/wordcamp-budgets-dashboard.css', __DIR__ ),
</span><span class="cx" style="display: block; padding: 0 10px"> array(),
</span><span class="cx" style="display: block; padding: 0 10px"> 1
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ if ( $hook == 'budgets_page_wcb-import-export' ) {
+ wp_enqueue_script( 'jquery-ui-datepicker' );
+ wp_enqueue_style( 'jquery-ui' );
+ wp_enqueue_style( 'wp-datepicker-skins' );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -61,7 +1000,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> function format_amount( $amount, $currency ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $formatted_amount = '';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $amount = \WordCamp_Budgets::validate_amount( $amount );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $amount = \WordCamp_Budgets::validate_amount( $amount );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( false === strpos( $currency, 'null' ) && $amount ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $formatted_amount = sprintf( '%s %s', number_format( $amount, 2 ), $currency );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -93,8 +1032,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> function convert_currency( $from, $to, $amount ) {
</span><span class="cx" style="display: block; padding: 0 10px"> global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $from = strtolower( $from );
- $to = strtolower( $to );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $from = strtolower( $from );
+ $to = strtolower( $to );
</ins><span class="cx" style="display: block; padding: 0 10px"> $cache_key = md5( sprintf( 'wcp-exchange-rate-%s:%s', $from, $to ) );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $rate = 0;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -106,7 +1045,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $url = add_query_arg( 'q', rawurlencode( $wpdb->prepare( 'select * from yahoo.finance.xchange where pair = %s', $from . $to ) ), $url );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $request = wp_remote_get( esc_url_raw( $url ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $body = json_decode( wp_remote_retrieve_body( $request ), true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $body = json_decode( wp_remote_retrieve_body( $request ), true );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! empty( $body['query']['results']['rate']['Ask'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $rate = floatval( $body['query']['results']['rate']['Ask'] );
</span></span></pre>
</div>
</div>
</body>
</html>