Index: civicrm/trunk/sites/all/modules/contribution_audit/contribution_audit.info |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +name = CiviAudit |
| 3 | +description = Auditing framework for contacts and contributions |
| 4 | +core = 6.x |
| 5 | +package = CiviAudit |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/contribution_audit/contribution_audit.module |
— | — | @@ -0,0 +1,82 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * include common queue2civicrm functions |
| 5 | + */ |
| 6 | +require_once( drupal_get_path( 'module', 'queue2civicrm' ) . '/queue2civicrm_common.inc' ); |
| 7 | + |
| 8 | +/** |
| 9 | + * Implementation of hook_perm(). |
| 10 | + */ |
| 11 | +function contribution_audit_perm() { |
| 12 | + return array('administer contribution_audit'); |
| 13 | +} |
| 14 | + |
| 15 | +/** |
| 16 | + * Implementation of hook_menu() |
| 17 | + */ |
| 18 | +function contribution_audit_menu() { |
| 19 | + $items = array(); |
| 20 | + |
| 21 | + $items['admin/settings/contribution_audit'] = array( |
| 22 | + 'title' => 'Contribution Audit Configuration', |
| 23 | + 'description' => t('Configure contribution audit settings.'), |
| 24 | + 'access arguments' => array('administer contribution_audit'), |
| 25 | + 'page callback' => 'drupal_get_form', |
| 26 | + 'page arguments' => array( 'contribution_audit_settings' ), |
| 27 | + ); |
| 28 | + |
| 29 | + return $items; |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * Callback for menu path "admin/settings/wmf_owa". |
| 34 | + */ |
| 35 | +function contribution_audit_settings() { |
| 36 | + return t( 'Contribution Audit settings.' ); |
| 37 | +} |
| 38 | + |
| 39 | +function contribution_audit_allocate_unallocated_contribs( $start_date, $end_date ) { |
| 40 | + $allocated = array(); // country => $ |
| 41 | + // find the info from missing trxns |
| 42 | + $missing_trxns = contribution_audit_find_missing_trxns( $start_date, $end_date ); |
| 43 | + |
| 44 | + foreach ( $missing_trxns as $missing_trxn ) { |
| 45 | + // what happens if we still dont have country data? |
| 46 | + if ( !strlen($missing_trxn[ 'country' ])) { |
| 47 | + $missing_trxn[ 'country' ] = 'unknown'; |
| 48 | + } |
| 49 | + _contribution_audit_aggregate_country_amounts( $allocated, $missing_trxn ); |
| 50 | + } |
| 51 | + return $allocated; |
| 52 | +} |
| 53 | + |
| 54 | +function _contribution_audit_aggregate_country_amounts( &$allocated, $trxn ) { |
| 55 | + // handle currency conversion, etc |
| 56 | + $trxn = _queue2civicrm_normalize_contrib_amnts( $missing_trxn ); |
| 57 | + |
| 58 | + if ( !in_array( $trxn[ 'country' ], array_keys( $allocated ))) { |
| 59 | + $allocated[ $trxn[ 'country' ]] = $trxn[ 'gross' ]; |
| 60 | + } else { |
| 61 | + $allocated[ $trxn[ 'country' ]] += $trxn[ 'gross' ]; |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Hook to find missing transactions |
| 67 | + * @return array containing the missing transactions |
| 68 | + */ |
| 69 | +function contribution_audit_find_missing_trxns( $start_date, $end_date ) { |
| 70 | + $missing_trxns = array(); |
| 71 | + module_invoke_all( 'contribution_audit_find_missing_trxns', $missing_trxns, $start_date, $end_date ); |
| 72 | + return $missing_trxns; |
| 73 | +} |
| 74 | + |
| 75 | +/** |
| 76 | + * Hook to find transactions missing country info |
| 77 | + * @return array contaning transactions missing country info |
| 78 | + */ |
| 79 | +function contribution_audit_find_trxns_missing_country_info() { |
| 80 | + $trxns_missing_country_info = array(); |
| 81 | + module_invoke_all( 'contribution_audit_find_missing_trxns', $trxns_missing_country_info ); |
| 82 | + return $trxns_missing_country_info; |
| 83 | +} |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/log_audit/log_audit.info |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +name = Log Audit |
| 3 | +description = Auditing framework for contacts and contributions in IPN listener logs |
| 4 | +core = 6.x |
| 5 | +package = CiviAudit |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/log_audit/log_audit.module |
— | — | @@ -0,0 +1,33 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +define( 'CONTRIBUTION_AUDIT_LOG_AUDIT_DIR', '/usr/local/src/log_audit/' ); |
| 5 | + |
| 6 | +/** |
| 7 | + * Implementation of hook_menu() |
| 8 | + */ |
| 9 | +function log_audit_menu() { |
| 10 | + $items = array(); |
| 11 | + |
| 12 | + $items['admin/settings/contribution_audit/log_audit'] = array( |
| 13 | + 'title' => 'Log Audit Configuration', |
| 14 | + 'description' => t('Configure log audit settings.'), |
| 15 | + 'access arguments' => array('administer contribution_audit'), |
| 16 | + 'page callback' => 'drupal_get_form', |
| 17 | + 'page arguments' => array( 'log_audit_settings' ), |
| 18 | + ); |
| 19 | + |
| 20 | + return $items; |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * Callback for menu path "admin/settings/wmf_owa". |
| 25 | + */ |
| 26 | +function log_audit_settings() { |
| 27 | + $form[ 'log_audit_dir' ] = array( |
| 28 | + '#type' => 'textfield', |
| 29 | + '#title' => t( 'Path to directory containing log audit scripts' ), |
| 30 | + '#required' => TRUE, |
| 31 | + '#default_value' => variable_get( 'log_audit_dir', CONTRIBUTION_AUDIT_LOG_AUDIT_DIR ), |
| 32 | + ); |
| 33 | + return system_settings_form( $form ); |
| 34 | +} |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/paypal_audit/paypal_audit.module |
— | — | @@ -0,0 +1,177 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +define( 'CONTRIBUTION_AUDIT_PAYFLOW_AUDIT_DIR', '/usr/local/src/payflow_audit/' ); |
| 5 | +require_once( 'paypal_audit.php' ); |
| 6 | + |
| 7 | +/** |
| 8 | + * Implementation of hook_menu() |
| 9 | + */ |
| 10 | +function paypal_audit_menu() { |
| 11 | + $items = array(); |
| 12 | + |
| 13 | + $items['admin/settings/contribution_audit/paypal_audit'] = array( |
| 14 | + 'title' => 'Paypal Audit Configuration', |
| 15 | + 'description' => t('Configure paypal audit settings.'), |
| 16 | + 'access arguments' => array('administer contribution_audit'), |
| 17 | + 'page callback' => 'drupal_get_form', |
| 18 | + 'page arguments' => array( 'paypal_audit_settings' ), |
| 19 | + ); |
| 20 | + |
| 21 | + return $items; |
| 22 | +} |
| 23 | + |
| 24 | +/** |
| 25 | + * Callback for menu path "admin/settings/wmf_owa". |
| 26 | + */ |
| 27 | +function paypal_audit_settings() { |
| 28 | + $form[ 'paypal_audit_dir' ] = array( |
| 29 | + '#type' => 'textfield', |
| 30 | + '#title' => t( 'Path to directory containing Payflow audit scripts' ), |
| 31 | + '#required' => TRUE, |
| 32 | + '#default_value' => variable_get( 'paypal_audit_dir', CONTRIBUTION_AUDIT_PAYFLOW_AUDIT_DIR ), |
| 33 | + ); |
| 34 | + return system_settings_form( $form ); |
| 35 | +} |
| 36 | + |
| 37 | +/** |
| 38 | + * Implementation of hook_contribution-audit_find_missing_trxns |
| 39 | + * @param array $trxns |
| 40 | + */ |
| 41 | +function paypal_audit_contribution_audit_find_missing_trxns( &$trxns, $start_date, $end_date ) { |
| 42 | + $pp_trxns = array(); |
| 43 | + $pf_trxns = array(); |
| 44 | + $missing_trxns = array(); |
| 45 | + |
| 46 | + // fetch trxns from paypal |
| 47 | + $options = array(); |
| 48 | + $audit = new Paypal_Audit(); |
| 49 | + $report = $audit->getCustomReport( $start_date, $end_date, $options ); |
| 50 | + |
| 51 | + // find the 'tender type' and trxn id columns so we can tell paypal v payflow |
| 52 | + $columns = $report->getReportResponse()->findColumnNumber( array('Tender Type', 'Transaction ID', 'PayPal Transaction ID' )); |
| 53 | + |
| 54 | + //loop through trxns (each trxn is a reportDataRow object), isolate paypal v non-paypal |
| 55 | + foreach( $pp_trxns->getData() as $trxn ) { |
| 56 | + if ( $trxn[ $tt_column ] == 'PayPal' ) { |
| 57 | + $pp_trxns[ $columns[ 'PayPal Transaction ID' ]] = iterator_to_array( $trxn ); |
| 58 | + } else { |
| 59 | + $pf_trxns[ $columns[ 'Transaction ID' ]] = iterator_to_array( $trxn ); |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + //check for missing non-paypal |
| 64 | + $pp_trxns_missing = paypal_audit_find_missing_pp_trxns( $pp_trxns ); |
| 65 | + array_push( $missing_trxns, paypal_audit_format_trxns( $pp_trxns_missing, $report, 'PayPal' )); |
| 66 | + |
| 67 | + //check for missing paypal |
| 68 | + $pf_trxns_missing = paypal_audit_find_missing_pf_trxns( $pf_trxns ); |
| 69 | + array_push( $missing_trxns, paypal_audit_format_trxns( $pf_trxns_missing, $report )); |
| 70 | + |
| 71 | + // add to $trxns and return |
| 72 | + array_merge( $trxns, $missing_trxns_formatted); |
| 73 | +} |
| 74 | + |
| 75 | +/** |
| 76 | + * Find PayPal transactions not recorded in civicrm_contribution |
| 77 | + * @TODO refactor (look at paypal_audit_find_missin_pf_trxns) |
| 78 | + * @param array $pp_trxns |
| 79 | + * @return array Missing transactions with full trxn data row objects |
| 80 | + */ |
| 81 | +function paypal_audit_find_missing_pp_trxns( $pp_trxns ) { |
| 82 | + $missing_trxns = array(); |
| 83 | + |
| 84 | + $dbs = _queue2civicrm_get_dbs(); |
| 85 | + $dbs->use_civicrm(); |
| 86 | + |
| 87 | + // check for matching ids - if no match, it's missing |
| 88 | + foreach ( array_keys( $pp_trxns ) as $pp_trxn_id ) { |
| 89 | + $query = "SELECT {trxn_id} |
| 90 | + FROM civicrm_contribution |
| 91 | + WHERE trxn_id LIKE 'PAYPAL %s' OR trxn_id LIKE 'RECURRING PAYPAL %s'"; |
| 92 | + $result = db_query( $query, $pp_trxn_id . "%" ); |
| 93 | + if ( !$result->rowCount() ) array_push( $missing_trxns, $pp_trxns[ $pp_trxn_id ] ); |
| 94 | + } |
| 95 | + |
| 96 | + $dbs->use_default(); |
| 97 | + return $missing_trxns; |
| 98 | +} |
| 99 | + |
| 100 | +/** |
| 101 | + * Find PayflowPro tranactions not recorded in civicrm_contribution |
| 102 | + * @TODO refactor and find more optimal way to search trxn ids |
| 103 | + * eg fix trxn_id column in db to ONLY store the id, then we can |
| 104 | + * do easier, faster searching like IN( ) |
| 105 | + * @param array $pf_trxns |
| 106 | + * @return array Missing trxns with full trxn data row objects |
| 107 | + */ |
| 108 | +function paypal_audit_find_missing_pf_trxns( $pf_trxns ) { |
| 109 | + $missing_trxns = array(); |
| 110 | + |
| 111 | + $dbs = _queue2civicrm_get_dbs(); |
| 112 | + $dbs->use_civicrm(); |
| 113 | + |
| 114 | + // check for matching ids - if it doesnt match, it's missing |
| 115 | + foreach ( array_keys( $pf_trxns ) as $pf_trxn_id ) { |
| 116 | + $query = "SELECT {trxn_id} FROM civicrm_contribution WHERE trxn_id LIKE 'PAYFLOWPRO %s'"; |
| 117 | + $result = db_query( $query, $pf_trxn_id . "%" ); |
| 118 | + if ( !$result->rowCount() ) array_push( $missing_trxns, $pf_trxns[ $pf_trxn_id ] ); |
| 119 | + } |
| 120 | + |
| 121 | + $dbs->use_default(); |
| 122 | + return $missing_trxns; |
| 123 | +} |
| 124 | + |
| 125 | +/** |
| 126 | + * Format transactions |
| 127 | + * @FIXME handle wonky fields (eg contribution tracking id, optout, etc) |
| 128 | + * @TODO finish the mappings! NOTE that some transactional information |
| 129 | + * will need to be fetched out of the minfraud or other logs... |
| 130 | + * @param unknown_type $trxns |
| 131 | + */ |
| 132 | +function paypal_audit_format_trxns( $trxns, $reprot_obj, $gateway='PayflowPro' ) { |
| 133 | + $trxns_formatted = array(); |
| 134 | + |
| 135 | + /** |
| 136 | + * {"contribution_tracking_id":"4983928","optout":"0","anonymous":"1","comment":null,"email":"xmarquez.b@gmail.com","size":null,"premium_language":null,"first_name":"Xavier","last_name":"Marquez Barreto","street_address":"Pto Azul Mz A-5 Villa 3","supplemental_address_1":null,"city":"Guayaquil","state_province":"Guayas","country":"EC","postal_code":"752","last_name_2":"Barreto","first_name_2":"Xavier Marquez","street_address_2":"Pto Azul Mz A-5 Villa 3","supplemental_address_2":null,"city_2":"Guayaquil","state_province_2":"Guayas","country_2":"EC","postal_code_2":"752","gateway":"paypal","gateway_txn_id":"6N71347241984711L","original_currency":"USD","original_gross":"3.00","fee":"0.39","gross":"3.00","net":2.61,"date":1303195718} |
| 137 | + */ |
| 138 | + $map = array( |
| 139 | + "contribution_tracking_id" => '', |
| 140 | + 'optout' => '', |
| 141 | + 'anonymous' => '', |
| 142 | + 'comment' => '', |
| 143 | + 'email' => '', |
| 144 | + 'first_name' => '', |
| 145 | + 'last_name' => '', |
| 146 | + 'street_address' => '', |
| 147 | + 'city' => '', |
| 148 | + 'state_province' => '', |
| 149 | + 'postal_code' => '', |
| 150 | + 'country' => 'Billing Country', |
| 151 | + 'gateway' => '', |
| 152 | + 'gateway_txn_id' => ( $gateway == 'PayflowPro' ) ? 'Transaction ID' : 'PayPal Transaction ID', |
| 153 | + 'original_currency' => 'Currency Symbol', |
| 154 | + 'original_gross' => '', |
| 155 | + 'fee' => '', |
| 156 | + 'gross' => 'Amount', |
| 157 | + 'fee' => '', |
| 158 | + 'net' => '', |
| 159 | + 'date' => 'Settled Date', |
| 160 | + ); |
| 161 | + |
| 162 | + foreach ( $trxns as $trxn ) { |
| 163 | + foreach( $map as $key => $value ) { |
| 164 | + if ( !strlen( $value )) { |
| 165 | + $formatted_trxn[ $key ] = ''; |
| 166 | + } else { |
| 167 | + $colNum = $report->getReportResponse()->findColumnNumber( $value ); |
| 168 | + if ( $key == 'date' ) { |
| 169 | + $formatted_trxn[ $key ] = strtotime( $trxn[ $colNum ] ); |
| 170 | + } else { |
| 171 | + $formatted_trxn[ $key ] = $trxn[ $colNum ]; |
| 172 | + } |
| 173 | + } |
| 174 | + array_push( $trxns_formatted, $formatted_trxn ); |
| 175 | + } |
| 176 | + |
| 177 | + return $trxns_formatted; |
| 178 | +} |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/paypal_audit/paypal_audit.php |
— | — | @@ -0,0 +1,34 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * A class to facilitate interaction with Paypal Audit scripts |
| 5 | + * @author arthur |
| 6 | + */ |
| 7 | +class Paypal_Audit { |
| 8 | + |
| 9 | + /** |
| 10 | + * Run custom reports from PayPal audit scripts |
| 11 | + * @param string $start_date |
| 12 | + * @param string $end_date |
| 13 | + * @param array $options |
| 14 | + */ |
| 15 | + public function getCustomReport( $start_date, $end_date, $options ) { |
| 16 | + require_once( variable_get( 'paypal_audit_dir', CONTRIBUTION_AUDIT_PAYFLOW_AUDIT_DIR ) . "PayflowReportTypes.php"); |
| 17 | + |
| 18 | + $time_bounds = array( $this->fixTime( $start_date ), $this->fixTime( $endDate )); |
| 19 | + |
| 20 | + $report = new CustomReport( $time_bounds ); |
| 21 | + $report_options = $report->getOptions( true ); |
| 22 | + $report->setOptions( array_push( $options, $report_options )); |
| 23 | + $report->runReport(); |
| 24 | + |
| 25 | + return $results; |
| 26 | + } |
| 27 | + |
| 28 | + /** |
| 29 | + * Format time correctly for reports |
| 30 | + * @param string Representation of date/time |
| 31 | + */ |
| 32 | + public function fixTime( $time ) { |
| 33 | + return date( strtotime( 'Y-m-d', strtotime( $time ))); |
| 34 | + } |
| 35 | +} |
\ No newline at end of file |
Index: civicrm/trunk/sites/all/modules/paypal_audit/paypal_audit.info |
— | — | @@ -0,0 +1,4 @@ |
| 2 | +name = PayPal Audit |
| 3 | +description = Auditing framework for contacts and contributions in PayPal services |
| 4 | +core = 6.x |
| 5 | +package = CiviAudit |
\ No newline at end of file |