Odyssey
hcuACH.i
1 <?php
2 /*
3  * File: hcuACH.i
4  *
5  * Purpose: Contains helper functions that are used with the ACH feature. Can be called
6  * from the .prg or the .data files.
7  *
8  * Use: This script should be included at the top of every ACH script using require_once
9  *
10  * Standard format: Each function should return a FALSE if there is an error or non-successful
11  * operation or a structure with the desired return information.
12  * Alternative format: Return a structure with a code = "000" for success, and a list of
13  * errors in an array.
14  *
15  * ACH Partner Types: '$'=Payroll, 'e'=Payee, 'r'=payor, ''=unassigned.
16  * Philosophy: show the default for the function but allow user to see them all.
17  *
18  * NOTES:
19  * 1. Terminology: ODFI is the Originating financial institution; RDFI is the Receiving financial
20  * institution. The Origin/Receiving is with respect to the money flow.
21  * 2. The RDFI needs to know if it is a checking or savings account. This is told with a
22  * transaction code.
23  * 3. The RDFI needs to know if the transaction is a credit or a debit. This is also told with
24  * a transaction code.
25  * 4. The address information of the remote entity needs to be captured in case the remote
26  * entity needs to be tracked down.
27  * 5. If any errors are used in multiple places append a unique number for better identification.
28  * 6. The <cu>transhdr.transaction code has specific meaning
29  * - For internal transfers it is the code sent to the core
30  * - For ACH transactions use for determining the Service Class Code and Standard Entry Class Codes:
31  * Service Class Code:
32  * 1X = credit with respect to the RDFI
33  * 2X = debit with respect to RDFI
34  * 3X = mixed credit/debit with respect to RDFI
35  * Standard Entry Class Code
36  * X = P (PPD), C (CCD), W (WEB)
37  * PPD/CCD are for ACH feature; WEB is for External Transfer Collection feature (PPD is External Payment)
38  *
39  */
40 define( "ACH_PARTNER_TYPE_PAYEE", "e" );
41 define( "ACH_PARTNER_TYPE_PAYOR", "r" );
42 define( "ACH_PARTNER_TYPE_UNASSIGNED", "" );
43 define( "ACH_PARTNER_TYPE_PAYROLL", "$" );
44 
45 define( "TEMPLATE_TYPE_PAYMENT", "P" );
46 define( "TEMPLATE_TYPE_COLLECTION", "C" );
47 define( "TEMPLATE_TYPE_PAYROLL", "$" );
48 
49 // Check the menuing features to verify this feature is available to this credit union.
50 function Check_ACHEnabled( $pDbh, $pHBEnv ) {
51  try {
52  } catch (Exception $ex) {
53  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
54  }
55 
56  return true;
57 } // end Check_ACHEnabled
58 
59 /**
60  * Get the list of partners and types for the current user's group.
61  *
62  * @param array $pDbh -- Current database handle
63  * @param array $pHBEnv -- Current server environment
64  *
65  * @return array -- An array with partner information
66  */
67 function ACH_GetPartners( $pDbh, $pHBEnv ) {
68  // initialize the return array
69  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
70 
71  try {
72  $SQL = "SELECT id, display_name, ach_name, partner_type, address, dfi_data, email_notify
73  FROM {$pHBEnv["Cu"]}achpartner
74  WHERE group_id = (SELECT group_id FROM {$pHBEnv["Cu"]}user WHERE user_id = {$pHBEnv["Uid"]})
75  ORDER BY display_name";
76 
77  $rs = db_query( $SQL, $pDbh );
78 
79  // cycle through the rows
80  $row = 0;
81  $return = array();
82  while ( $partnerRow = db_fetch_assoc( $rs, $row++ ) ) {
83  // unassigneds get empty string
84  $partnerRow["partner_type"] = trim( $partnerRow["partner_type"] );
85  $partnerRow["address"] = HCU_JsonDecode( $partnerRow["address"] );
86  $partnerRow["dfi_data"] = HCU_JsonDecode( $partnerRow["dfi_data"] );
87 
88  $return[] = $partnerRow;
89  }
90 
91  $returnData["data"] = $return;
92  } catch (Exception $ex) {
93  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
94  $returnData["code"] = "999";
95  }
96 
97  return $returnData;
98 } // end ACH_GetPartners
99 
100 /**
101  * Return all the data for the specified ACH partner.
102  *
103  * @param array $pDbh -- Current database handle
104  * @param array $pHBEnv -- Current server environment
105  * @param array $pPartnerId -- If moving money from internal account, or to internal account
106  *
107  * @return array -- An array with success code or error information
108  */
109 function ACH_GetSinglePartner( $pDbh, $pHBEnv, $pPartnerId ) {
110  // initialize the return array
111  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
112 
113  try {
114  $SQL = "SELECT * FROM {$pHBEnv["Cu"]}achpartner WHERE id = {$pPartnerId}";
115 
116  $rs = db_query( $SQL, $pDbh );
117 
118  $partnerRow = db_fetch_assoc( $rs, 0 );
119 
120  // expand any JSON data
121  $addressData = HCU_JsonDecode( $partnerRow["address"], true );
122  $dfiData = HCU_JsonDecode( $partnerRow["dfi_data"], true );
123 
124  // put the return data together
125  $basicInfo = array();
126  $basicInfo["id"] = $partnerRow["id"];
127  $basicInfo["ach_name"] = $partnerRow["ach_name"];
128  $basicInfo["display_name"] = $partnerRow["display_name"];
129  $basicInfo["email_notify"] = $partnerRow["email_notify"] == 't' ? true : false;
130  $basicInfo["partner_type"] = trim( $partnerRow["partner_type"] );
131 
132  $returnData["data"] = array_merge( $basicInfo, $addressData, $dfiData );
133 
134  } catch (Exception $ex) {
135  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
136  $returnData["code"] = "999";
137  }
138 
139 
140  return $returnData;
141 } // end ACH_GetSinglePartner
142 
143 /**
144  * Get the accounts that can be used for the given ACH operation.
145  *
146  * @param integer $pDbh -- Current database handle
147  * @param array $pHBEnv -- Current HB_ENV values
148  * @param object $pFromTo -- "from" if wanting source accounts, "to" if destination accouts
149  *
150  * @return array -- An array with the correct accounts
151  */
152 function ACH_GetAccounts( $pDbh, $pHBEnv, $pFromTo ) {
153  // initialize the return array
154  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
155 
156  try {
157  $Fset = HCU_array_key_exists("Fset", $pHBEnv) ? intval($pHBEnv["Fset"]) : 0;
158  $Fset2 = HCU_array_key_exists("Fset2", $pHBEnv) ? intval($pHBEnv["Fset2"]) : 0;
159 
160  // must setup an order by to match the TX_list function
161  // this way the list will match the profile descriptions
162  // screen and any other screens on banking.
163 
164  // since this is a union, loannumber and accounttype are
165  // displayed in the accounttype column. we will use a case
166  // statement in the sql to determine which way to trim accounttype.
167  $orderBy = "";
168  $orderBy .= "recordtype, display_order,";
169  $orderBy .= " CASE recordtype";
170  if ($Fset & GetFlagsetValue('CU_SORTORDER4')) {
171  $orderBy .= " WHEN 'D' THEN btrim(accounttype, 'DIS')";
172  } else {
173  $orderBy .= " WHEN 'D' THEN accounttype";
174  }
175 
176  $orderBy .= " END,";
177  $orderBy .= " certnumber";
178 
179  if ( $pFromTo === "withdrawl" ) {
180  // get the deposit accounts can take from and loans that can be added onto
181  $SQL = "
182  SELECT * FROM (
183  SELECT u.accountnumber, u.accounttype, u.certnumber, recordtype, display_name, display_order, view_balances, description, available AS balance
184  FROM {$pHBEnv["Cu"]}useraccounts u
185  INNER JOIN {$pHBEnv["Cu"]}accountbalance a ON a.accountnumber = u.accountnumber
186  AND a.accounttype = u.accounttype
187  AND a.certnumber = u.certnumber
188  AND a.may_withdraw = 't'
189  WHERE u.user_id = {$pHBEnv["Uid"]}
190  AND u.ext_withdraw = 't'
191  AND u.recordtype = 'D'
192  ) accts ORDER BY $orderBy";
193  } else if ( $pFromTo === "deposit" ) {
194  // get the deposit accounts can deposit into and loans that can have payments made into
195  $SQL = "
196  SELECT * FROM (
197  SELECT u.accountnumber, u.accounttype, u.certnumber, recordtype, display_name, display_order, view_balances, description, available AS balance
198  FROM {$pHBEnv["Cu"]}useraccounts u
199  INNER JOIN {$pHBEnv["Cu"]}accountbalance a ON a.accountnumber = u.accountnumber
200  AND a.accounttype = u.accounttype
201  AND a.certnumber = u.certnumber
202  AND a.may_deposit = 't'
203  WHERE u.user_id = {$pHBEnv["Uid"]}
204  AND u.ext_deposit = 't'
205  AND u.recordtype = 'D'
206  ) accts ORDER BY $orderBy";
207  } else {
208  throw new Exception( "Invalid operation" );
209  }
210 
211  $rs = db_query( $SQL, $pDbh );
212 
213  // cycle through the rows
214  $row = 0;
215  $return = array();
216  while ( $accountRow = db_fetch_array( $rs, $row++ ) ) {
217  // Overloaded accounts should not be allowed for ach commercial
218  // due to the fact that the referenced account may not actually
219  // exist.
220 
221  // Check for the overloaded accounttype, skip this account.
222  $isOverloadedAry = explode("@", $accountRow['accounttype']);
223  $isOverloaded = count($isOverloadedAry) > 1;
224 
225  if ($isOverloaded) {
226  continue;
227  }
228 
229  // Account not overlaoded, add to list.
230  $coreDescription = trim( $accountRow["description"] );
231  if ( strlen( trim( $accountRow["accounttype"] ) ) > 0 ) {
232  $coreDescription .= " - " . $accountRow["accounttype"];
233  }
234 
235  // only save the ones allowed to use
236  $accountNumber = trim( $accountRow["accountnumber"] );
237  $accountType = trim( $accountRow["accounttype"] );
238  $certNumber = trim( $accountRow["certnumber"] );
239  $acctId = "{$accountRow["recordtype"]}|{$accountNumber}|{$accountType}|{$certNumber}";
240  $acctIdEncrypted = hcu_encrypturl( $acctId, $pHBEnv['historyHash'] );
241  $balanceStr = $accountRow["view_balances"] == 't' ? "$" . $accountRow["balance"] : "N/A";
242  $saveRow = array( "account" => $accountNumber,
243  "account_name" => getAccountDescription($pDbh, $pHBEnv["Cu"], $accountRow["accountnumber"], $accountRow["description"], $accountRow["accounttype"],
244  $accountRow["display_name"], $pHBEnv["Fset3"], $certNumber, false),
245  "acctid" => $acctIdEncrypted,
246  "amount" => $balanceStr );
247 
248  $return[] = $saveRow;
249  }
250 
251  $returnData["data"] = $return;
252  } catch (Exception $ex) {
253  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
254  $returnData["code"] = "999";
255  }
256 
257  return $returnData;
258 } // end ACH_GetAccounts
259 
260 
261 /**
262  * Check the inputs for valid data. Note that some inputs are optional and not checked
263  * because the inputs were sanitized.
264  *
265  * @param array $pInputVars -- Current database handle
266  *
267  * @return array -- An array with success code or error information
268  */
269 function ACH_ValidateInputs( $pHBEnv, $pDbh, $pInputVars, $pMC ) {
270  // initialize the return array
271  $aryReturnErrors = array();
272  $retACHValidate = array( "code" => "000", "errors" => array(), "data" => array() );
273 
274  // check required inputs common to all ACH operations
275  if ( trim( $pInputVars["internal_acct"] ) == "" ) {
276  $aryReturnErrors[] = $pMC->msg("ACH Validation Local Account") . " 1029";
277  }
278 
279  if ( trim( $pInputVars["internal_sub_acct"] ) == "" ) {
280  $aryReturnErrors[] = $pMC->msg("ACH Validation Local Account") . " 1016";
281  }
282 
283  // test the sub account string - should have three or four parts
284  $acctIdDecrypted = hcu_decrypturl( $pInputVars["internal_sub_acct"], $pHBEnv['historyHash'] );
285  $accountParts = explode( "|", $acctIdDecrypted ); // eg "D|1103|10|0
286 
287  // get account information
288  $localAccount = TX_list($pHBEnv['dbh'], $pHBEnv, $acctIdDecrypted);
289 
290  if ( count( $accountParts ) < 3 || count( $accountParts ) > 4 ) {
291  $aryReturnErrors[] = $pMC->msg("ACH Validation Local Account") . " 1017";
292  }
293 
294  if ( trim( $pInputVars["eff_date"] ) == "" ) {
295  $aryReturnErrors[] = $pMC->msg("ACH Validation Eff Date Missing") . " 1030";
296  } else {
297  $timeTest = strtotime($pInputVars["eff_date"]);
298  if ( !intval( $timeTest ) ) {
299  $aryReturnErrors[] = $pMC->msg("ACH Validation Eff Date") . " 1031";
300  } else {
301  $today = strtotime( date( "m/d/Y" ) );
302  if ( $timeTest < $today ) {
303  $aryReturnErrors[] = $pMC->msg("ACH Validation Eff Date Today") . " 1032";
304  }
305  }
306  }
307 
308  if ( !(strtoupper( trim( $pInputVars["ach_type"] ) ) === "PPD" ||
309  strtoupper( trim( $pInputVars["ach_type"] ) ) === "CCD") ) {
310  $aryReturnErrors[] = $pMC->msg("ACH Validation ACH Type") . " 1033";
311  }
312 
313  // check inputs specific to an operation
314  if ( $pInputVars["action"] == "submit_payment" ||
315  $pInputVars["action"] == "submit_collection" ) {
316  // NOTE: Partner id may not exist if the partner is being added as part of the ACH transaction
317 
318  // can only check if name exists because we don't know if being saved or not
319  $achName = trim( $pInputVars["ach_name"] ) == "" ? $pInputVars["display_name"] : $pInputVars["ach_name"];
320  if ( strlen( trim( $achName ) ) == 0 ) {
321  $aryReturnErrors[] = $pMC->msg("ACH Validation Name", HCU_DISPLAY_AS_HTML) . " 1043";
322  }
323 
324  if ( trim( $pInputVars["dfi_routing"] ) == "" ||
325  !ctype_digit( trim( $pInputVars["dfi_routing"] ) ) ||
326  strlen( trim( $pInputVars["dfi_routing"] ) ) != 9 ) {
327  $aryReturnErrors[] = $pMC->msg("ACH Validation DFI Routing") . " 1034";
328  }
329 
330  if ( trim( $pInputVars["dfi_account"] ) == "" ) {
331  $aryReturnErrors[] = $pMC->msg("ACH Validation DFI Account") . " 1035";
332  }
333 
334  if ( !(trim( $pInputVars["dfi_account_type"] ) == ACCOUNT_TYPE_CHECKING ||
335  trim( $pInputVars["dfi_account_type"] ) == ACCOUNT_TYPE_SAVINGS )) {
336  $aryReturnErrors[] = $pMC->msg("ACH Validation DFI Type") . " 1036";
337  }
338 
339  if ( trim( $pInputVars["amount"] ) == "" || $pInputVars["amount"] == 0) {
340  $aryReturnErrors[] = $pMC->msg("ACH Validation Amount") . " 1037";
341  }
342 
343  // if submitting a payment transaction, must check available
344  // funds for the selected sub account. Only make this check
345  // for non-scheduled.
346  $scheduled = $pInputVars['frequency'] !== "OneTime";
347  if (!$scheduled && $pInputVars["action"] == "submit_payment") {
348 
349  if ($localAccount["acctlist"]["balance"] < $pInputVars["amount"]) {
350  $aryReturnErrors[] = $pMC->msg("Transfer exceeds") . " 1050";
351  }
352  }
353 
354  } else if ( $pInputVars["action"] == "submit_batch_payment" ||
355  $pInputVars["action"] == "submit_batch_collection" ) {
356  if ( trim( $pInputVars["template_id"] ) == "" || intval( trim( $pInputVars["template_id"] ) ) == 0 ) {
357  $aryReturnErrors[] = $pMC->msg("ACH Validation Template") . " 1038";
358  }
359 
360  // validate the batch data - each id should have an amount and optionally the addenda and notify
361  $batchData = html_entity_decode( trim( $pInputVars["batch_data"], ENT_QUOTES ) );
362 
363  if ( !strlen( $batchData ) ) {
364  $aryReturnErrors[] = $pMC->msg("ACH Validation Batch Data Missing") . " 1039";
365  } else {
366  $batchInfo = HCU_JsonDecode( $batchData, true );
367 
368  if ( !count( $batchInfo ) ) {
369  $aryReturnErrors[] = $pMC->msg("ACH Validation Batch Data Invalid") . " 1040";
370  } else {
371  $batchPartnerTotal = 0;
372  for ( $i = 0; $i < count( $batchInfo ); $i++ ) {
373  // name is already in the partner list so only checking if there is an id and amount
374  $batchPartnerId = $batchInfo[$i]["id"];
375  $batchPartnerAmount = $batchInfo[$i]["amount"];
376  $batchPartnerTotal += $batchPartnerAmount;
377 
378  if ( !is_int( $batchPartnerId ) && !( $batchPartnerId > 0 ) ) {
379  $aryReturnErrors[] = $pMC->msg("ACH Validation Batch PartnerId") . " 1041";
380  // stop looking
381  break;
382  }
383 
384  if ( !is_numeric( $batchPartnerAmount ) && !( $batchPartnerAmount > 0 ) ) {
385  $aryReturnErrors[] = $pMC->msg("ACH Validation Batch Partner Amount") . " 1042";
386  // stop looking
387  break;
388  }
389  }
390 
391  // payments canot exceed available funds
392  if ( $pInputVars["action"] == "submit_batch_payment" ) {
393  if ( $localAccount["acctlist"]["balance"] < $batchPartnerTotal ) {
394  $aryReturnErrors[] = $pMC->msg("Transfer exceeds") . " 1051";
395  }
396  }
397  }
398  }
399  }
400 
401  if ( count( $aryReturnErrors ) > 0 ) {
402  // * validation errors occurred
403  $retACHValidate['code'] = '999';
404  $retACHValidate["errors"] = $aryReturnErrors;
405  }
406 
407  return $retACHValidate;
408 } // end ACH_ValidateInputs
409 
410 /**
411  * Validate the address information that is used with ACH Partners. Provide valid
412  * values for any missing or incorrect information. These should have already passed
413  * any sanitization.
414  *
415  * @param array $pInputVars -- Sanitized address from the client
416  *
417  * @return array -- An array with validated info
418  */
419 function privValidateAddress( $pHBEnv, $pInputVars ) {
420  // initialize the return array
421  $pOutputVars = array( "address1" => "",
422  "address2" => "",
423  "city" => "",
424  "state" => "",
425  "zip" => "",
426  "country" => "",
427  "email" => "" );
428  try {
429  // test each part
430  $address = $pInputVars["address1"];
431  if ( !$address ) {
432  $address = "";
433  }
434  $pOutputVars["address1"] = $address;
435 
436  $address2 = $pInputVars["address2"];
437  if ( !$address2 ) {
438  $address2 = "";
439  }
440  $pOutputVars["address2"] = $address2;
441 
442  $city = $pInputVars["city"];
443  if ( !$city ) {
444  $city = "";
445  }
446  $pOutputVars["city"] = $city;
447 
448  $state = $pInputVars["state"];
449  if ( !$state ) {
450  $state = "";
451  }
452  $pOutputVars["state"] = $state;
453 
454  $zip = $pInputVars["zip"];
455  if ( !$zip ) {
456  $zip = "";
457  }
458  $pOutputVars["zip"] = $zip;
459 
460  $country = $pInputVars["country"];
461  if ( !$country ) {
462  $country = "";
463  }
464  $pOutputVars["country"] = $country;
465 
466  $email = filter_var($pInputVars["email"], FILTER_VALIDATE_EMAIL);
467  if ( $email === false ) {
468  $email = "";
469  }
470  $pOutputVars["email"] = $email;
471 
472  } catch (Exception $ex) {
473  // just return the defaults
474  }
475 
476  return $pOutputVars;
477 } // privValidateAddress
478 
479 /**
480  * Validate the display name that is used with ACH Partners. This should have already passed
481  * any sanitization. Display name should be unique.
482  *
483  * @param array $pInputVars -- Sanitized address from the client
484  *
485  * @return string -- An error string if there is one
486  */
487 function privValidateName( $pDbh, $pHBEnv, $pInputVars, $pMC ) {
488  // initialize the return string
489  $retString = "";
490 
491  try {
492  // name is only added during a single payment or collection
493  if ( trim( $pInputVars["display_name"] ) == "" ) {
494  throw new Exception( $pMC->msg("ACH Validation Name", HCU_DISPLAY_AS_HTML) . " 1022" );
495  }
496 
497  // validate the display name is unique (should be sanitized)
498  // put in the form it is saved
499  $displayName = prep_save( html_entity_decode( trim( $pInputVars["display_name"] ), ENT_QUOTES ) );
500  $sql = "SELECT id FROM {$pHBEnv["Cu"]}achpartner WHERE display_name = '{$displayName}'";
501  $rs = db_query( $sql, $pDbh );
502 
503  $row = 0;
504  $partnerIdList = array();
505  while ( $partnerRow = db_fetch_assoc( $rs, $row++ ) ) {
506  // unassigneds get empty string
507  $partnerIdList[] = $partnerRow["id"];
508  }
509 
510  // if found one it better be this one
511  if ( (count( $partnerIdList ) > 1) ||
512  (count( $partnerIdList ) == 1 && intval( $pInputVars["partner_id"] ) == 0) ) {
513  throw new Exception( $pMC->msg("ACH Validation Unique Display Name", HCU_DISPLAY_AS_HTML) . " 1009" );
514  } else if ( count( $partnerIdList ) == 1 ) {
515  // case where user is changing a name
516  if ( $partnerIdList[0] != $pInputVars["partner_id"] ) {
517  throw new Exception( $pMC->msg("ACH Validation Unique Display Name", HCU_DISPLAY_AS_HTML) . " 1010" );
518  }
519  } // if none found then it is unique
520 
521 
522  } catch (Exception $ex) {
523  // just return the error message
524  $retString = $ex->getMessage();
525  }
526 
527  return $retString;
528 } // privValidateName
529 
530 /**
531  * Determine the transaction code based on the operation and PPD vs CCD.
532  *
533  * @param string $pFeatureCode
534  * @param string $pACHType
535  *
536  * @return string $transCode
537  */
538 function ACH_GetTransCode( $pFeatureCode, $pACHType ) {
539  $achType = strtoupper( $pACHType );
540  if ( $pFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ) {
541  // this is a Credit to the RDFI
542  $transCode = ( $achType == "PPD" ? "1P" : ( $achType == "CCD" ? "1C" : "1X" ) );
543  } else {
544  // this is a Debit to the RDFI
545  $transCode = ( $achType == "PPD" ? "2P" : ( $achType == "CCD" ? "2C" : "2X" ) );
546  }
547 
548  return $transCode;
549 } // ACH_GetTransCode
550 
551 /**
552  * Submit an ACH payment by adding the entry to the table.
553  *
554  * @param array $pDbh -- Current database handle
555  * @param array $pHBEnv -- Current server environment
556  * @param array $pInputVars -- Validated data from the client
557  *
558  * @return array -- An array with success code or error information
559  */
560 function ACH_Submit( $pDbh, $pHBEnv, $pInputVars, $pMC ) {
561  // initialize the return array
562  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
563 
564  $achFeatureCode = $pInputVars["action"] === "submit_payment" ? constant( "FEATURE_ACH_PAYMENTS" ) : constant( "FEATURE_ACH_COLLECTIONS" );
565 
566  try {
567  // gather any information we need
568 
569  // start a transaction
570  $sql = "BEGIN TRANSACTION";
571  $achRs = db_query( $sql, $pDbh );
572 
573  $acctIdDecrypted = hcu_decrypturl( $pInputVars["internal_sub_acct"], $pHBEnv['historyHash'] );
574  $accountParts = explode( "|", $acctIdDecrypted ); // eg "D|1103|10|0
575  $account = $accountParts[1];
576  $accountType = $accountParts[2];
577 
578  // get the effective date
579  $effDateTime = strtotime( $pInputVars["eff_date"] );
580  $nowDateTime = strtotime( date( "Y/m/d" ) );
581 
582  if ( $effDateTime === false ||
583  $effDateTime < $nowDateTime ) {
584  $effDateTime = time();
585  }
586 
587  $effDate = date( "Y-m-d", $effDateTime );
588 
589  // same as the deposit type in <cu>useraccounts, so get to right account table
590  $depositType = $accountParts[0];
591 
592  // for ACH transactions the $transactionCode is determined by the feature code and type of ach transaction
593  $transactionCode = ACH_GetTransCode( $achFeatureCode, $pInputVars["ach_type"] );
594 
595  $memo = prep_save( html_entity_decode( trim( $pInputVars["memo"] ), ENT_QUOTES ) );
596 
597  // see if need approval
598  if ( $pInputVars["needs_confirmation"] ) {
599  $approvedBy = "NULL";
600  $approvedDate = "NULL";
601  $approvedStatus = "NULL";
602  } else {
603  $approvedBy = $pHBEnv["Uid"];
604  $approvedDate = "now()";
605  $approvedStatus = "10";
606  }
607 
608  // Setup transaction metadata.
609  // Single or Recurring transaction, and the current interval. In this case
610  // the record is non-recurring.
611  $transMeta = array();
612  $transMeta["source"] = "immed";
613  $transMeta["recurr"] = "no";
614  $transMeta["interval"] = 0;
615  $jsonTransMeta = HCU_JsonEncode($transMeta);
616  $jsonTransMeta = prep_save($jsonTransMeta);
617 
618  // set up the query for the transaction header
619  $sql = "INSERT INTO {$pHBEnv["Cu"]}transhdr (feature_code, effective_date,
620  posted_by, posted_date, approved_by, approved_date, approved_status,
621  accountnumber, transactioncode, memo, transmeta)
622  VALUES ('{$achFeatureCode}', '{$effDate}',
623  {$pHBEnv["Uid"]}, now(), $approvedBy, $approvedDate, $approvedStatus,
624  '{$account}', '$transactionCode', '$memo', '{$jsonTransMeta}' )
625  RETURNING id, posted_date, approved_date";
626 
627  $hdrRs = db_query( $sql, $pDbh );
628 
629  if ( !$hdrRs ) {
630  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1001" );
631  }
632 
633  // use the insert id from the header in the detail
634  list($headerId, $postedDate, $approvedDate) = db_fetch_array($hdrRs, 0);
635 
636  if ( !$headerId ) {
637  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1002" );
638  }
639 
640  // set up the actual ach data
641  $achName = trim( $pInputVars["ach_name"] ) == "" ? $pInputVars["display_name"] : $pInputVars["ach_name"];
642  $achName = prep_save(hcu_displayHtml( preg_replace( "/[\`\;]/", "", $achName ) ) );
643 
644  // here, we are only interested that there is a name; uniqueness is checked elsewhere, earlier, if name is being saved
645  if ( strlen( $achName ) == 0 ) {
646  throw new Exception( $pMC->msg("ACH Validation Name", HCU_DISPLAY_AS_HTML) . " 1019" );
647  }
648 
649  $addressVars = privValidateAddress( $pHBEnv, $pInputVars );
650 
651  // sanity check to make sure email notification is set up and there wasn't an incorrect email
652  if ( strlen( $pInputVars["email"] ) > 0 && !strlen( $addressVars["email"] ) ) {
653  throw new Exception( $pMC->msg("Email appears invalid", HCU_DISPLAY_AS_RAW) . " 1023" );
654  } else if ( isset( $pInputVars['email_notify'] ) && $pInputVars['email_notify'] == 1 && !strlen( $addressVars["email"] ) ) {
655  throw new Exception( $pMC->msg("EMail Missing", HCU_DISPLAY_AS_RAW) . " 1020" );
656  }
657 
658 
659  // "rdfi" is the receving dfi
660  // NOTE: currently (03/17/17) all the detail is either credit or debit
661  $rdfiTransCode = ( $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ? "CR" : "DB" );
662  $achData = array( "rdfi" => array( "rdfi_routing" => $pInputVars["dfi_routing"],
663  "rdfi_account" => $pInputVars["dfi_account"],
664  "rdfi_account_type" => $pInputVars["dfi_account_type"],
665  "rdfi_txn_type" => $rdfiTransCode,
666  "addenda" => prep_save( html_entity_decode( $pInputVars["addenda"], ENT_QUOTES ) ) ),
667  "remote_entity" => array( "name" => $achName,
668  "address1" => prep_save( html_entity_decode( $pInputVars["address1"], ENT_QUOTES ) ),
669  "address2" => prep_save( html_entity_decode( $pInputVars["address2"], ENT_QUOTES ) ),
670  "email" => $pInputVars["email"] ) );
671 
672  // set up the "from" and "to" info
673  if ( $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ) {
674  // local to remote
675  $sourceAccountInfo = $acctIdDecrypted;
676  $sourceAccountKey = array(
677  "accountnumber"=>$accountParts[1],
678  "recordtype"=>$accountParts[0]
679  );
680  if ($sourceAccountKey['recordtype'] == "D") {
681  $sourceAccountKey['accounttype'] = $accountParts[2];
682  $sourceAccountKey['certnumber'] = $accountParts[3];
683  } else if ($sourceAccountKey['recordtype'] == "L") {
684  $sourceAccountKey['loannumber'] = $accountParts[2];
685  }
686 
687  $destAccountInfo = $achData;
688  $destAccountKey = array();
689  } else {
690  // remote to local
691  $sourceAccountInfo = $achData;
692  $sourceAccountKey = array();
693 
694  $destAccountInfo = $acctIdDecrypted;
695  $destAccountKey = array(
696  "accountnumber"=>$accountParts[1],
697  "recordtype"=>$accountParts[0]
698  );
699  if ($destAccountKey['recordtype'] == "D") {
700  $destAccountKey['accounttype'] = $accountParts[2];
701  $destAccountKey['certnumber'] = $accountParts[3];
702  } else if ($destAccountKey['recordtype'] == "L") {
703  $destAccountKey['loannumber'] = $accountParts[2];
704  }
705  }
706 
707  // set up the transfer data (matching how TRNEXT saves info)
708  $transData = array( "acct_source" => $sourceAccountInfo, "acct_dest" => $destAccountInfo, "source_key"=>$sourceAccountKey, "dest_key"=>$destAccountKey );
709 
710  $jsonTransData = HCU_JsonEncode( $transData );
711  $jsonTransDataWithMeta = AddAccountMetaData($pHBEnv, $achFeatureCode, $transactionCode, $jsonTransData);
712 
713  // need to get amount back into currency form
714  $amount = $pInputVars['amount'];
715  $emailNotify = isset( $pInputVars['email_notify'] ) ? intval($pInputVars['email_notify']) : 0;
716 
717  // use the partner id for now
718  $referenceId = isset( $pInputVars['partner_id'] ) && intval( $pInputVars["partner_id"] ) > 0 ? intval( $pInputVars['partner_id'] ) : 0;
719 
720  // add the transaction detail
721  // reference_id is a quasi-foreigh key to either the ach partner, external accounts, or wire partner table
722  $sql = "INSERT INTO {$pHBEnv["Cu"]}transdtl (transhdr_id, reference_id, amount, email_notify, transdata)
723  VALUES ($headerId, $referenceId, $amount, '$emailNotify', '$jsonTransDataWithMeta')";
724 
725  $dtlRs = db_query( $sql, $pDbh );
726 
727  if ( !$dtlRs ) {
728  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1003" );
729  }
730 
731  // commit the transaction
732  $sql = "COMMIT TRANSACTION";
733  $achRs = db_query( $sql, $pDbh );
734 
735  // return success
736  // return confirmation code
737  // build a confirmation code (so don't need to read the database)
738  $confCodeBuilder["id"] = $headerId;
739  if ($pInputVars["needs_confirmation"]) {
740  $confCodeBuilder["posted_by"] = $pHBEnv["Uid"];
741  $confCodeBuilder["posted_date"] = $postedDate;
742  $confirmationCode = GetTransferConfirmCode( $pHBEnv, "post", $confCodeBuilder );
743  } else {
744  $confCodeBuilder["approved_by"] = $pHBEnv["Uid"];
745  $confCodeBuilder["approved_date"] = $approvedDate;
746  $confirmationCode = GetTransferConfirmCode( $pHBEnv, "approve", $confCodeBuilder );
747  }
748 
749  // Return data to display
750  $myDateTime = new DateTime( $postedDate );
751  $myDateTime->setTimezone(new DateTimeZone($pHBEnv["tz"]));
752  $displayDate = $myDateTime->format("m/d/Y g:ia");
753  $returnData['data']['data_date'] = $displayDate;
754  $returnData['data']['data_amount'] = $amount;
755  $returnData['data']['data_confirm'] = $confirmationCode;
756 
757  $companyData = ACH_GetCompanyData($pHBEnv);
758  $returnData['data']['data_company'] = $companyData['group_name'];
759 
760  $returnData['data']['data_action'] = (
761  $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ?
762  $pMC->msg('ACH Payment Title', HCU_DISPLAY_AS_RAW) :
763  $pMC->msg('ACH Collection Title', HCU_DISPLAY_AS_RAW)
764  );
765 
766  // Must grab account description and display_name
767  // to build correct account description.
768  $acctDesc = (
769  $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ?
770  GetMemberDescription($pDbh, $pHBEnv['Cu'], $pHBEnv['Uid'], $sourceAccountKey) :
771  GetMemberDescription($pDbh, $pHBEnv['Cu'], $pHBEnv['Uid'], $destAccountKey)
772  );
773  $returnData['data']['data_account'] = getAccountDescription(
774  $pDbh, $pHBEnv["Cu"],
775  $accountParts[1],
776  $acctDesc['description'],
777  $accountParts[2],
778  $acctDesc["display_name"],
779  $pHBEnv["Fset3"], $accountParts[3], false
780  );
781 
782  } catch (Exception $ex) {
783  // capture the exception
784  $message = $ex->getMessage();
785  $code = $ex->getCode();
786 
787  // roll back the transaction
788  $sql = "ROLLBACK TRANSACTION";
789  db_query( $sql, $pDbh );
790 
791  $logInfo = array( "message" => $message, "code" => $code );
792  $returnData["code"] = "999";
793  $returnData["errors"][] = $message;
794  }
795 
796  return $returnData;
797 
798 } // end ACH_Submit
799 
800 /**
801  * Submit an ACH payment by adding the entry to the table.
802  *
803  * @param array $pDbh -- Current database handle
804  * @param array $pHBEnv -- Current server environment
805  * @param array $pInputVars -- Validated data from the client
806  * @param array $pBatchInfo -- Validated batch information
807  *
808  * @return array -- An array with success code or error information
809  */
810 function ACH_SubmitBatch( $pDbh, $pHBEnv, $pInputVars, $pBatchInfo, $pMC ) {
811  // initialize the return array
812  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
813 
814  if ( $pInputVars["action"] === "submit_payment" ||
815  $pInputVars["action"] === "submit_batch_payment" ) {
816  $achFeatureCode = constant( "FEATURE_ACH_PAYMENTS" );
817  } else {
818  $achFeatureCode = constant( "FEATURE_ACH_COLLECTIONS" );
819  }
820 
821  try {
822  // gather any information we need
823 
824  // start a transaction
825  $sql = "BEGIN TRANSACTION";
826  $achRs = db_query( $sql, $pDbh );
827 
828  // get the template for later update
829  $sql = "SELECT tmpl_meta
830  FROM {$pHBEnv["Cu"]}achtemplate
831  WHERE tmpl_id = {$pInputVars['template_id']}";
832 
833  $rs = db_query( $sql, $pDbh );
834 
835  // get the template information
836  $templateRow = db_fetch_assoc( $rs, 0 );
837 
838  // expand the meta information
839  $currentMetaInfo = HCU_JsonDecode( $templateRow["tmpl_meta"], true );
840 
841  // start processing the batch
842  $acctIdDecrypted = hcu_decrypturl( $pInputVars["internal_sub_acct"], $pHBEnv['historyHash'] );
843  $accountParts = explode( "|", $acctIdDecrypted ); // eg "D|1103|10|0
844  $account = $accountParts[1];
845 
846  // build source_key and dest_key values
847  // for the transdtl.transdata records
848  $accountKey = array(
849  "accountnumber" => $accountParts[1],
850  "recordtype" => $accountParts[0],
851  "accounttype" => $accountParts[2],
852  "certnumber" => $accountParts[3]
853  );
854 
855  // get the effective date
856  $effDateTime = strtotime( $pInputVars["eff_date"] );
857  $nowDateTime = strtotime( date( "Y/m/d" ) );
858 
859  if ( $effDateTime === false ||
860  $effDateTime < $nowDateTime ) {
861  $effDateTime = time();
862  }
863 
864  $effDate = date( "Y-m-d", $effDateTime );
865 
866  // same as the deposit type in <cu>useraccounts, so get to right account table
867  $depositType = $accountParts[0];
868 
869  // for ACH transactions the $transactionCode is determined by the feature code and type of ach transaction
870  $transactionCode = ACH_GetTransCode( $achFeatureCode, $pInputVars["ach_type"] );
871 
872  $memo = prep_save( html_entity_decode( trim( $pInputVars["memo"] ), ENT_QUOTES ) );
873 
874  // see if need approval
875  if ( $pInputVars["needs_confirmation"] ) {
876  $approvedBy = "NULL";
877  $approvedDate = "NULL";
878  $approvedStatus = "NULL";
879  } else {
880  $approvedBy = $pHBEnv["Uid"];
881  $approvedDate = "now()";
882  $approvedStatus = "10";
883  }
884 
885  // Setup transaction metadata.
886  // Single or Recurring transaction, and the current interval. In this case
887  // the record is non-recurring.
888  $transMeta = array();
889  $transMeta["source"] = "immed";
890  $transMeta["recurr"] = "no";
891  $transMeta["interval"] = 0;
892  $jsonTransMeta = HCU_JsonEncode($transMeta);
893  $jsonTransMeta = prep_save($jsonTransMeta);
894 
895  // set up the query for the transaction header
896  $sql = "INSERT INTO {$pHBEnv["Cu"]}transhdr (feature_code, effective_date,
897  posted_by, posted_date, approved_by, approved_date, approved_status,
898  accountnumber, transactioncode, memo, transmeta)
899  VALUES ('{$achFeatureCode}', '{$effDate}',
900  {$pHBEnv["Uid"]}, now(), $approvedBy, $approvedDate, $approvedStatus, '{$account}',
901  '$transactionCode', '{$memo}', '{$jsonTransMeta}' )
902  RETURNING id, posted_date, approved_date";
903 
904  $hdrRs = db_query( $sql, $pDbh );
905 
906  if ( !$hdrRs ) {
907  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1004" );
908  }
909 
910  // use the insert id from the header in the detail
911  list($headerId, $postedDate, $approvedDate) = db_fetch_array($hdrRs, 0);
912 
913  if ( !$headerId ) {
914  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1005" );
915  }
916 
917  // set up each batch transaction
918  $totalAmount = 0;
919  for ( $i = 0; $i < count( $pBatchInfo ); $i++ ) {
920  // get the partner id
921  $partnerId = $pBatchInfo[$i]["id"];
922 
923  // get the financial information
924  $sql = "SELECT * FROM {$pHBEnv["Cu"]}achpartner WHERE id = {$partnerId}";
925 
926  $rs = db_query( $sql, $pDbh );
927 
928  $partnerRow = db_fetch_assoc( $rs, 0 );
929 
930  // set up the actual ach data
931  $achName = trim( $partnerRow["ach_name"] ) == "" ? $partnerRow["display_name"] : $partnerRow["ach_name"];
932  $achName = prep_save(hcu_displayHtml( preg_replace( "/[\`\;]/", "", $achName ) ) );
933 
934  // expand any JSON data
935  $dfiData = HCU_JsonDecode( $partnerRow["dfi_data"], true );
936  $addressData = HCU_JsonDecode( $partnerRow["address"], true );
937 
938  // sanity check to make sure email notification is set up and there is an email
939  if ( isset( $pBatchInfo[$i]['notify'] ) && $pBatchInfo[$i]['notify'] == 1 && !strlen( $addressData["email"] ) ) {
940  throw new Exception( $pMC->msg("Please specify email address", HCU_DISPLAY_AS_RAW) . " 1021" );
941  }
942 
943  // get the address lines
944  $address1 = isset( $pInputVars["address1"] ) ? trim( $pInputVars["address1"] ) : "";
945  $address2 = isset( $pInputVars["address2"] ) ? trim( $pInputVars["address2"] ) : "";
946 
947  // "rdfi" is the receving dfi
948  // NOTE: currently (03/17/17) all the detail is either credit or debit
949  $rdfiTransCode = ( $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ? "CR" : "DB" );
950  // addenda is the only thing coming from the client
951  $achData = array( "rdfi" => array( "rdfi_routing" => $dfiData["dfi_routing"],
952  "rdfi_account" => $dfiData["dfi_account"],
953  "rdfi_account_type" => $dfiData["dfi_account_type"],
954  "rdfi_txn_type" => $rdfiTransCode,
955  "addenda" => $pBatchInfo[$i]["addenda"] ),
956  "remote_entity" => array( "name" => $achName,
957  "address1" => prep_save( html_entity_decode( $address1, ENT_QUOTES ) ),
958  "address2" => prep_save( html_entity_decode( $address2, ENT_QUOTES ) ),
959  "email" => $addressData["email"] ) );
960 
961  // set up the "from" and "to" info
962  if ( $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ) {
963  // local to remote
964  $sourceAccountInfo = $acctIdDecrypted;
965  $destAccountInfo = $achData;
966 
967  // local account is source
968  // destination is empty
969  $sourceKey = $accountKey;
970  $destKey = array();
971  } else {
972  // remote to local
973  $sourceAccountInfo = $achData;
974  $destAccountInfo = $acctIdDecrypted;
975 
976  // local account is destination
977  // source key is empty
978  $sourceKey = array();
979  $destKey = $accountKey;
980  }
981 
982  // set up the transfer data (matching how TRNEXT saves info)
983  $transData = array( "acct_source" => $sourceAccountInfo, "acct_dest" => $destAccountInfo, "source_key" => $sourceKey, "dest_key" => $destKey );
984 
985  $jsonTransData = HCU_JsonEncode( $transData );
986  $jsonTransDataWithMeta = AddAccountMetaData($pHBEnv, $achFeatureCode, $transactionCode, $jsonTransData);
987 
988  $amount = floatval( $pBatchInfo[$i]['amount'] );
989  $totalAmount += $amount;
990  $emailNotify = isset( $pBatchInfo[$i]['notify'] ) ? intval($pBatchInfo[$i]['notify']) : 0;
991 
992  // use the ach partner id as the reference
993  $referenceId = $partnerRow['id'];
994 
995  // add the transaction detail
996  // reference_id is a quasi-foreigh key to either the ach partner, external accounts, or wire partner table
997  $sql = "INSERT INTO {$pHBEnv["Cu"]}transdtl (transhdr_id, reference_id, amount, email_notify, transdata)
998  VALUES ($headerId, $referenceId, $amount, '$emailNotify', '$jsonTransDataWithMeta')";
999 
1000  $dtlRs = db_query( $sql, $pDbh );
1001 
1002  if ( !$dtlRs ) {
1003  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1006" );
1004  }
1005  }
1006 
1007  // get the current time
1008  $myDateTime = new DateTime();
1009  $myDateTime->setTimezone(new DateTimeZone( $pHBEnv["tz"] ));
1010  $formattedTime = $myDateTime->format("m/d/y g:ia T");
1011 
1012  // update the template meta data
1013  $currentMetaInfo["type"] = $pInputVars["ach_type"];
1014  $currentMetaInfo["last_proc"] = $formattedTime;
1015  $templateMeta = HCU_JsonEncode( $currentMetaInfo );
1016 
1017  if ( !$templateMeta ) {
1018  throw new Exception( $pMC->msg("ACH Error Encoding", HCU_DISPLAY_AS_HTML) . " 1045" );
1019  }
1020 
1021  $SQL = "UPDATE {$pHBEnv["Cu"]}achtemplate SET tmpl_meta = '$templateMeta' WHERE tmpl_id = {$pInputVars['template_id']}";
1022  $rs = db_query( $SQL, $pDbh );
1023 
1024  // commit the transaction
1025  $sql = "COMMIT TRANSACTION";
1026  $achRs = db_query( $sql, $pDbh );
1027 
1028  // return success
1029  // return confirmation code
1030  // build a confirmation code (so don't need to read the database)
1031  $confCodeBuilder["id"] = $headerId;
1032  if ($pInputVars["needs_confirmation"]) {
1033  $confCodeBuilder["posted_by"] = $pHBEnv["Uid"];
1034  $confCodeBuilder["posted_date"] = $postedDate;
1035  $confirmationCode = GetTransferConfirmCode( $pHBEnv, "post", $confCodeBuilder );
1036  } else {
1037  $confCodeBuilder["approved_by"] = $pHBEnv["Uid"];
1038  $confCodeBuilder["approved_date"] = $approvedDate;
1039  $confirmationCode = GetTransferConfirmCode( $pHBEnv, "approve", $confCodeBuilder );
1040  }
1041 
1042  // Return data to display
1043  $myDateTime = new DateTime( $postedDate );
1044  $myDateTime->setTimezone(new DateTimeZone($pHBEnv["tz"]));
1045  $displayDate = $myDateTime->format("m/d/Y g:ia");
1046  $returnData['data']['data_date'] = $displayDate;
1047  $returnData['data']['data_amount'] = $totalAmount;
1048  $returnData['data']['data_confirm'] = $confirmationCode;
1049 
1050  $companyData = ACH_GetCompanyData($pHBEnv);
1051  $returnData['data']['data_company'] = $companyData['group_name'];
1052 
1053  $returnData['data']['data_action'] = (
1054  $achFeatureCode == constant( "FEATURE_ACH_PAYMENTS" ) ?
1055  $pMC->msg('ACH Payment Title', HCU_DISPLAY_AS_RAW) :
1056  $pMC->msg('ACH Collection Title', HCU_DISPLAY_AS_RAW)
1057  );
1058 
1059  // Must grab account description and display_name
1060  // to build correct account description.
1061  $acctDesc = GetMemberDescription($pDbh, $pHBEnv['Cu'], $pHBEnv['Uid'], $accountKey);
1062  $returnData['data']['data_account'] = getAccountDescription(
1063  $pDbh, $pHBEnv["Cu"],
1064  $accountParts[1],
1065  $acctDesc['description'],
1066  $accountParts[2],
1067  $acctDesc["display_name"],
1068  $pHBEnv["Fset3"], $accountParts[3], false
1069  );
1070 
1071  } catch (Exception $ex) {
1072  // capture the exception
1073  $message = $ex->getMessage();
1074  $code = $ex->getCode();
1075 
1076  // roll back the transaction
1077  $sql = "ROLLBACK TRANSACTION";
1078  db_query( $sql, $pDbh );
1079 
1080  $logInfo = array( "message" => $message, "code" => $code );
1081  $returnData["code"] = "999";
1082  $returnData["errors"][] = $message;
1083  }
1084 
1085  return $returnData;
1086 
1087 } // end ACH_SubmitBatch
1088 
1089 /**
1090  * Update ACH partner information.
1091  * If it is a new partner and there is a template, add the partner to the template
1092  *
1093  * @param array $pDbh -- Current database handle
1094  * @param array $pHBEnv -- Current server environment
1095  * @param array $pInputVars -- Validated data from the client
1096  *
1097  * @return array -- An array with success code or error information
1098  */
1099 function ACH_UpdatePartner( $pDbh, $pHBEnv, $pInputVars, $pMC ) {
1100  // initialize the return array
1101  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1102 
1103  try {
1104  // use what the user sent, or blank for unassigned
1105  $partnerType = "";
1106 
1107  if ( $pInputVars["partner_type"] === "e" ) {
1108  // payee
1109  $partnerType = "e";
1110  } else if ( $pInputVars["partner_type"] === "r" ) {
1111  // payor
1112  $partnerType = "r";
1113  } else if ( $pInputVars["partner_type"] === "$" ) {
1114  // payroll
1115  $partnerType = "$";
1116  }
1117 
1118  $return = privValidateName( $pDbh, $pHBEnv, $pInputVars, $pMC );
1119  if ( strlen( $return ) > 0 ) {
1120  throw new Exception( $return );
1121  }
1122 
1123  $addressVars = privValidateAddress( $pHBEnv, $pInputVars );
1124 
1125  // sanity check to make sure email notification is set up and there wasn't an incorrect email
1126  if ( strlen( $pInputVars["email"] ) > 0 && !strlen( $addressVars["email"] ) ) {
1127  throw new Exception( $pMC->msg("Email appears invalid", HCU_DISPLAY_AS_RAW) . " 1023" );
1128  } else if ( isset( $pInputVars['email_notify'] ) && $pInputVars['email_notify'] == 1 && !strlen( $addressVars["email"] ) ) {
1129  throw new Exception( $pMC->msg("EMail Missing", HCU_DISPLAY_AS_RAW) . " 1025" );
1130  }
1131 
1132  // build the address json
1133  $address = array( "address1" => prep_save( html_entity_decode( $addressVars["address1"], ENT_QUOTES ) ),
1134  "address2" => prep_save( html_entity_decode( $addressVars["address2"], ENT_QUOTES ) ),
1135  "city" => prep_save( html_entity_decode( $addressVars["city"], ENT_QUOTES ) ),
1136  "state" => $addressVars["state"],
1137  "zip" => $addressVars["zip"],
1138  "country" => $addressVars["country"],
1139  "email" => $addressVars["email"] );
1140  $jsonAddress = HCU_JsonEncode( $address );
1141 
1142  // build the DFI json
1143  // NOTE: don't care about the rdfi_txn_type since that is added when the txn is added to transdtl.
1144  $dfi = array( "dfi_routing" => $pInputVars["dfi_routing"],
1145  "dfi_account" => $pInputVars["dfi_account"],
1146  "dfi_account_type" => $pInputVars["dfi_account_type"] );
1147  $jsonDFI = HCU_JsonEncode( $dfi );
1148 
1149  // figure out the email notify flag
1150  $emailNotify = isset( $pInputVars['email_notify'] ) && $pInputVars['email_notify'] == 1 ? "true" : "false";
1151 
1152  $achName = prep_save( trim( html_entity_decode( $pInputVars["ach_name"], ENT_QUOTES ) ) );
1153 
1154  $displayName = prep_save( trim( html_entity_decode( $pInputVars["display_name"], ENT_QUOTES ) ) );
1155 
1156  if ( $achName == "" ) $achName = $displayName;
1157 
1158  // see if updating or adding the ach partner
1159  if ( $pInputVars["partner_id"] > 0 ) {
1160  $SQL = "UPDATE {$pHBEnv["Cu"]}achpartner
1161  SET partner_type = '{$partnerType}',
1162  ach_name = '{$achName}',
1163  display_name = '{$displayName}',
1164  email_notify = $emailNotify,
1165  address = '$jsonAddress',
1166  dfi_data = '$jsonDFI'
1167  WHERE id = {$pInputVars["partner_id"]}";
1168  } else {
1169  $SQL = "INSERT INTO {$pHBEnv["Cu"]}achpartner (group_id, partner_type, ach_name,
1170  display_name, email_notify, address, dfi_data)
1171  VALUES ( (SELECT group_id FROM {$pHBEnv["Cu"]}user WHERE user_id = {$pHBEnv["Uid"]}),
1172  '{$pInputVars["partner_type"]}', '{$pInputVars["ach_name"]}', '{$displayName}',
1173  '$emailNotify', '$jsonAddress', '$jsonDFI' )
1174  RETURNING id";
1175  }
1176 
1177  $achRs = db_query( $SQL, $pDbh );
1178 
1179  if ( !$achRs ) {
1180  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1007" );
1181  }
1182 
1183  // if working with a template, need to add a new partner to the template
1184  if ( $pInputVars["partner_id"] == 0 &&
1185  isset( $pInputVars["template_id"] ) &&
1186  intval( $pInputVars["template_id"] ) > 0 ) {
1187  // get the new partner id
1188  list($partnerId) = db_fetch_array($achRs, 0);
1189 
1190  // add to the template
1191  $result = ACH_AddPartnerToTemplate( $pDbh, $pHBEnv, $pInputVars["template_id"], $partnerId, $pMC );
1192  if ($result['code'] != '000') {
1193  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1008" );
1194  }
1195  } else if ( $pInputVars["partner_id"] == 0 ) {
1196  list($partnerId) = db_fetch_array($achRs, 0);
1197  } else {
1198  $partnerId = $pInputVars["partner_id"];
1199  }
1200 
1201  // return the partner id
1202  $return["partner_id"] = $partnerId;
1203 
1204  $returnData["data"] = $return;
1205 
1206  // return success
1207 
1208  } catch (Exception $ex) {
1209  // capture the exception
1210  $message = $ex->getMessage();
1211  $code = $ex->getCode();
1212 
1213  $logInfo = array( "message" => $message, "code" => $code );
1214  $returnData["code"] = "999";
1215  $returnData["errors"][] = $message;
1216  }
1217 
1218  return $returnData;
1219 } // end ACH_UpdatePartner
1220 
1221 
1222 /**
1223  * Delete ACH partner information.
1224  * If it is a new partner and there is a template, add the partner to the template
1225  *
1226  * @param array $pDbh -- Current database handle
1227  * @param array $pHBEnv -- Current server environment
1228  * @param array $pInputVars -- Validated data from the client
1229  *
1230  * @return array -- An array with success code or error information
1231  */
1232 function ACH_DeletePartner( $pDbh, $pHBEnv, $pInputVars, $pMC ) {
1233  // initialize the return array
1234  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1235 
1236  try {
1237 
1238  // check if partner is part of any batch templates.
1239  // do not allow delete if partner is part of any tempaltes.
1240  $sql = "
1241  SELECT * FROM (
1242  SELECT json_array_elements(tmpl_partner::json) AS partners
1243  FROM scrubcuachtemplate) AS partners
1244  WHERE partners::json->>'partner'='{$pInputVars["partner_id"]}'";
1245  $sqlRs = db_query($sql, $pDbh);
1246  if (!$sqlRs) {
1247  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1049" );
1248  }
1249  $tmplData = db_fetch_all($sqlRs);
1250 
1251  // if not templates use this partner. continue with delete
1252  if (!empty($tmplData) && count($tmplData) > 0) {
1253  throw new Exception($pMC->msg("ACH Error Delete Partner TE", HCU_DISPLAY_AS_HTML));
1254  }
1255 
1256  // check if partner is associated in any ach scheduled transcations
1257  // if any scheduled trasactions are found,do not allow delete.
1258  $sql = "
1259  SELECT * FROM cu_scheduledtxn WHERE txn_data::json->'txn'->>'from'='{$pInputVars["partner_id"]}'
1260  UNION
1261  SELECT * FROM cu_scheduledtxn WHERE txn_data::json->'txn'->>'to'='{$pInputVars["partner_id"]}'";
1262  $txnRs = db_query( $sql, $pDbh );
1263  if ( !$txnRs ) {
1264  throw new Exception( $pMC->msg("ACH Error Delete Partner SE", HCU_DISPLAY_AS_HTML));
1265  }
1266  $txnData = db_fetch_all($txnRs);
1267 
1268  // if no scheduled transactions are found, continue with delete.
1269  // else throw error to user.
1270  if (!empty($txnData) && count($txnData) > 0) {
1271  throw new Exception($pMC->msg("ACH Error Delete Partner SE", HCU_DISPLAY_AS_HTML));
1272  }
1273 
1274  $achSql = "
1275  DELETE FROM {$pHBEnv["Cu"]}achpartner
1276  WHERE id = {$pInputVars["partner_id"]}";
1277  $achSqlRs = db_query( $achSql, $pDbh );
1278  if ( !$achSql ) {
1279  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1044" );
1280  }
1281 
1282  $returnData['data'] = $pInputVars['partner_id'];
1283  } catch ( Exception $ex ) {
1284  // capture the exception
1285  $message = $ex->getMessage();
1286  $code = $ex->getCode();
1287 
1288  $logInfo = array( "message" => $message, "code" => $code );
1289  $returnData["code"] = "999";
1290  $returnData["errors"][] = $message;
1291  }
1292 
1293  return $returnData;
1294 }
1295 
1296 /**
1297  * Gather the necessary templates - ach partners, internal accounts for payments/collections.
1298  *
1299  * @param array $pDbh -- Current database handle
1300  * @param array $pHBEnv -- Current server environment
1301  * @param array $pAction -- To know which set of templates
1302  *
1303  * @return array -- An array with success code or error information
1304  */
1305 function ACH_GetTemplates( $pDbh, $pHBEnv, $pAction, $pMC ) {
1306  // initialize the return array
1307  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1308 
1309  try {
1310  $SQL = "SELECT at.*
1311  FROM {$pHBEnv["Cu"]}user u
1312  INNER JOIN {$pHBEnv["Cu"]}achtemplate at ON at.group_id = u.group_id
1313  WHERE u.user_id = {$pHBEnv["Uid"]}
1314  ORDER BY tmpl_name";
1315 
1316  $rs = db_query( $SQL, $pDbh );
1317 
1318  // cycle through the rows
1319  $row = 0;
1320  $return = array();
1321  while ( $templateRow = db_fetch_assoc( $rs, $row++ ) ) {
1322  // break out the ACH Type (ppd vs ccd, or blank)
1323  $tmplMeta = HCU_JsonDecode( $templateRow["tmpl_meta"], true );
1324  $achType = $tmplMeta["type"];
1325 
1326  $templateRow["ach_type"] = $achType;
1327 
1328  // unassigneds get empty string
1329  $return[] = $templateRow;
1330  }
1331 
1332  $returnData["data"] = $return;
1333  } catch (Exception $ex) {
1334  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
1335  $returnData["code"] = "999";
1336  $returnData["errors"] = $pMC->msg("ACH Error Reading Templates", HCU_DISPLAY_AS_HTML) . " 1026";
1337  }
1338 
1339  return $returnData;
1340 } // end ACH_GetTemplates
1341 
1342 
1343 /**
1344  * Gather the partner information for the given template - name, address, account.
1345  *
1346  * @param array $pDbh -- Current database handle
1347  * @param array $pHBEnv -- Current server environment
1348  * @param array $pTemplateId -- If moving money from internal account, or to internal account
1349  *
1350  * @return array -- An array with success code or error information
1351  */
1352 function ACH_GetTemplatePartners( $pDbh, $pHBEnv, $pTemplateId, $pMC ) {
1353  // initialize the return array
1354  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1355 
1356  try {
1357  $SQL = "SELECT tmpl_partner
1358  FROM {$pHBEnv["Cu"]}achtemplate
1359  WHERE tmpl_id = $pTemplateId";
1360 
1361  $rs = db_query( $SQL, $pDbh );
1362 
1363  // get the partner information
1364  $templateRow = db_fetch_assoc( $rs, 0 );
1365 
1366  // expand the partner info
1367  $partnerInfo = HCU_JsonDecode( $templateRow["tmpl_partner"], true );
1368 
1369  $return = array();
1370  if ( count( $partnerInfo ) > 0 ) {
1371  // cycle through the rows to create a list of ids
1372  $idList = array();
1373  for ( $i = 0; $i < count( $partnerInfo ); $i++ ) {
1374  $idList[] = $partnerInfo[$i]["partner"];
1375  }
1376 
1377  $idString = implode( ",", $idList );
1378  $SQL = "SELECT *
1379  FROM {$pHBEnv["Cu"]}achpartner
1380  WHERE id IN ($idString)
1381  ORDER BY display_name ";
1382 
1383  $rs = db_query( $SQL, $pDbh );
1384 
1385  $row = 0;
1386  while ( $partnerRow = db_fetch_assoc( $rs, $row++ ) ) {
1387  // unassigneds get empty string
1388  $partnerRow["partner_type"] = trim( $partnerRow["partner_type"] );
1389  // return boolean true
1390  // add the saved template information
1391  $partnerRow["last_amount"] = 0;
1392  $partnerRow["last_addenda"] = "";
1393 
1394  for ( $pi = 0; $pi < count( $partnerInfo ); $pi++ ) {
1395  if ( $partnerInfo[$pi]["partner"] == $partnerRow["id"] ) {
1396  $partnerRow["last_amount"] = $partnerInfo[$pi]["amount"];
1397  $partnerRow["last_addenda"] = $partnerInfo[$pi]["addenda"];
1398  // note: the "notify" comes from the ach partner info since it is kept up to date there
1399 
1400  break;
1401  }
1402  }
1403 
1404  $return[] = $partnerRow;
1405  }
1406  }
1407 
1408  $returnData["data"] = $return;
1409  } catch (Exception $ex) {
1410  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
1411  $returnData["code"] = "999";
1412  $returnData["errors"] = $pMC->msg("ACH Error Reading Template Partners", HCU_DISPLAY_AS_HTML) . " 1027";
1413  }
1414 
1415  return $returnData;
1416 } // end ACH_GetTemplatePartners
1417 
1418 /**
1419  * Add the partner to the template list. Use defaults for amounts, addenda, etc.
1420  *
1421  * @param array $pDbh -- Current database handle
1422  * @param array $pHBEnv -- Current server environment
1423  * @param array $pTemplateId -- ID of template
1424  * @param array $pPartnerId -- ID of partner
1425  *
1426  * @return array -- An array with success code or error information
1427  */
1428 function ACH_AddPartnerToTemplate( $pDbh, $pHBEnv, $pTemplateId, $pPartnerId, $pMC ) {
1429  // initialize the return array
1430  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1431 
1432  try {
1433  // get the partner information for the template
1434  $SQL = "SELECT email_notify, dfi_data
1435  FROM {$pHBEnv["Cu"]}achpartner
1436  WHERE id = $pPartnerId";
1437 
1438  $rs = db_query( $SQL, $pDbh );
1439 
1440  // get the partner information
1441  $partnerData = db_fetch_assoc( $rs, 0 );
1442 
1443  // expand the partner info
1444  $partnerDFI = HCU_JsonDecode( $partnerData["dfi_data"], true );
1445 
1446  $defaultAddenda = isset( $partnerDFI["addenda"] ) && strlen( $partnerDFI["addenda"] ) > 0 ? html_entity_decode( $partnerDFI["addenda"], ENT_QUOTES ) : "";
1447  $defaultEmailNotify = $partnerData["email_notify"] == "t" ? true : false;
1448 
1449  $SQL = "SELECT tmpl_partner, tmpl_meta
1450  FROM {$pHBEnv["Cu"]}achtemplate
1451  WHERE tmpl_id = $pTemplateId";
1452 
1453  $rs = db_query( $SQL, $pDbh );
1454 
1455  // get the template information
1456  $templateRow = db_fetch_assoc( $rs, 0 );
1457 
1458  // expand the partner info
1459  $partnerInfo = HCU_JsonDecode( $templateRow["tmpl_partner"], true );
1460 
1461  // add the partner
1462  $partnerInfo[] = array( "partner" => $pPartnerId,
1463  "notify" => $defaultEmailNotify,
1464  "amount" => "0.00",
1465  "addenda" => prep_save( $defaultAddenda ) );
1466 
1467  // update the template partner list
1468  $templatePartners = HCU_JsonEncode( $partnerInfo );
1469 
1470  // get the current time
1471  $myDateTime = new DateTime();
1472  $myDateTime->setTimezone(new DateTimeZone( $pHBEnv["tz"] ));
1473  $formattedTime = $myDateTime->format("m/d/y g:ia T");
1474 
1475  // update the meta data
1476  $metaInfo = HCU_JsonDecode( $templateRow["tmpl_meta"], true );
1477  $metaInfo["count"] = count( $partnerInfo );
1478  $metaInfo["last_edit"] = $formattedTime;
1479 
1480  $templateMeta = HCU_JsonEncode( $metaInfo );
1481 
1482 // Debugging error - need to add "!" back in front of $templateMeta
1483  if ( !$templatePartners || !$templateMeta ) {
1484  throw new Exception( $pMC->msg("ACH Error Encoding", HCU_DISPLAY_AS_HTML) . " 1013" );
1485  }
1486 
1487  $SQL = "UPDATE {$pHBEnv["Cu"]}achtemplate SET
1488  tmpl_partner = '$templatePartners',
1489  tmpl_meta = '$templateMeta'
1490  WHERE tmpl_id = $pTemplateId";
1491  $rs = db_query( $SQL, $pDbh );
1492 
1493  // return success
1494  } catch (Exception $ex) {
1495  $message = $ex->getMessage();
1496  $logInfo = array( "message" => $message, "code" => $ex->getCode() );
1497  if ( trim( $message ) == "" ) {
1498  $message = $pMC->msg("ACH Error Adding Template Partner", HCU_DISPLAY_AS_HTML) . " 1028";
1499  }
1500 
1501  $returnData["code"] = "999";
1502  $returnData["errors"] = $message;
1503  }
1504 
1505  return $returnData;
1506 } // end ACH_AddPartnerToTemplate
1507 
1508 /**
1509  * Add the partner to the template list. Use defaults for amounts, addenda, etc.
1510  *
1511  * @param array $pDbh -- Current database handle
1512  * @param array $pHBEnv -- Current server environment
1513  * @param array $pInputVars -- Un-validated data from the client
1514  *
1515  * @return array -- An array with success code or error information
1516  */
1517 function ACH_UpdateTemplate( $pDbh, $pHBEnv, $pInputVars, $pMC ) {
1518  // initialize the return array
1519  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1520 
1521  try {
1522  // common value
1523  $templateId = intval( $pInputVars["template_id"] );
1524 
1525  if ( $templateId == 0 ) {
1526  throw new Exception( $pMC->msg("ACH Validation Template", HCU_DISPLAY_AS_HTML) . " 1011" );
1527  }
1528 
1529  // determine the operation
1530  if ( $pInputVars["action"] === "update_template" ) {
1531  // sanitize the inputs
1532  $templateName = prep_save( html_entity_decode( $pInputVars["template_name"], ENT_QUOTES ) );
1533 
1534  if ( $templateId == -1 ) {
1535  // get the current time
1536  $myDateTime = new DateTime();
1537  $myDateTime->setTimezone(new DateTimeZone( $pHBEnv["tz"] ));
1538  $formattedTime = $myDateTime->format("m/d/y g:ia T");
1539 
1540 
1541  $defaultMeta = HCU_JsonEncode( array( "type" => "",
1542  "total" => "",
1543  "last_proc" => "",
1544  "last_edit" => $formattedTime,
1545  "count" => "0" ) );
1546  $defaultPartner = "[]";
1547 
1548  // NOTE: for now, tmpl_type is P(ayment) or C(ollection), but all templates are shown on either screen so it could change.
1549  $SQL = "INSERT INTO {$pHBEnv["Cu"]}achtemplate
1550  ( tmpl_name, group_id, tmpl_type, tmpl_meta, tmpl_partner )
1551  VALUES ( '$templateName', (SELECT group_id FROM {$pHBEnv["Cu"]}user WHERE user_id = {$pHBEnv["Uid"]}), '', '{$defaultMeta}', '{$defaultPartner}' )";
1552  } else {
1553  $SQL = "UPDATE {$pHBEnv["Cu"]}achtemplate SET tmpl_name = '$templateName' WHERE tmpl_id = $templateId";
1554  }
1555  } else if ( $pInputVars["action"] === "delete_template" ) {
1556  $SQL = "DELETE FROM {$pHBEnv["Cu"]}achtemplate WHERE tmpl_id = $templateId";
1557  }
1558 
1559  $rs = db_query( $SQL, $pDbh );
1560  if ( !$rs ) {
1561  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1018" );
1562  }
1563 
1564 
1565  // return success
1566  } catch (Exception $ex) {
1567  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
1568  $returnData["code"] = "999";
1569  $returnData["errors"] = $ex->getMessage();
1570  }
1571 
1572  return $returnData;
1573 } // end ACH_UpdateTemplate
1574 
1575 /**
1576  * Remove the partner from the template list.
1577  *
1578  * @param array $pDbh -- Current database handle
1579  * @param array $pHBEnv -- Current server environment
1580  * @param array $pTemplateId -- ID of template
1581  * @param array $pPartnerId -- ID of partner
1582  *
1583  * @return array -- An array with success code or error information
1584  */
1585 function ACH_RemovePartnerFromTemplate( $pDbh, $pHBEnv, $pTemplateId, $pPartnerId, $pMC ) {
1586  // initialize the return array
1587  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1588 
1589  try {
1590  if ( $pTemplateId == 0 ) {
1591  throw new Exception( $pMC->msg("ACH Validation Template", HCU_DISPLAY_AS_HTML) . " 1012" );
1592  }
1593 
1594  $SQL = "SELECT tmpl_partner, tmpl_meta
1595  FROM {$pHBEnv["Cu"]}achtemplate
1596  WHERE tmpl_id = $pTemplateId";
1597 
1598  $rs = db_query( $SQL, $pDbh );
1599 
1600  // get the template information
1601  $templateRow = db_fetch_assoc( $rs, 0 );
1602 
1603  // expand the partner info
1604  $partnerInfo = HCU_JsonDecode( $templateRow["tmpl_partner"], true );
1605 
1606  // find the partner (check through whole array in case of accidental duplicate)
1607  $found = false;
1608  $newPartnerList = array();
1609  for ( $i = 0; $i < count( $partnerInfo ); $i++ ) {
1610  if ( $partnerInfo[$i]["partner"] == $pPartnerId ) {
1611  $found = true;
1612  } else {
1613  $newPartnerList[] = $partnerInfo[$i];
1614  }
1615  }
1616 
1617  if ( $found ) {
1618  // update the template partner list
1619  $templatePartners = HCU_JsonEncode( $newPartnerList );
1620 
1621  // get the current time
1622  $myDateTime = new DateTime();
1623  $myDateTime->setTimezone(new DateTimeZone( $pHBEnv["tz"] ));
1624  $formattedTime = $myDateTime->format("m/d/y g:ia T");
1625 
1626  // update the meta data
1627  $metaInfo = HCU_JsonDecode( $templateRow["tmpl_meta"], true );
1628  $metaInfo["count"] = count( $newPartnerList );
1629  $metaInfo["last_edit"] = $formattedTime;
1630 
1631  $templateMeta = HCU_JsonEncode( $metaInfo );
1632 
1633 // Debugging error - need to add "!" back in front of $templateMeta
1634  if ( !$templatePartners || !$templateMeta ) {
1635  throw new Exception( $pMC->msg("ACH Error Encoding", HCU_DISPLAY_AS_HTML) . " 1014", 1014 );
1636  }
1637 
1638  $SQL = "UPDATE {$pHBEnv["Cu"]}achtemplate SET
1639  tmpl_partner = '$templatePartners',
1640  tmpl_meta = '$templateMeta'
1641  WHERE tmpl_id = $pTemplateId";
1642  $rs = db_query( $SQL, $pDbh );
1643  }
1644 
1645  // return success
1646  } catch (Exception $ex) {
1647  $message = $ex->getMessage();
1648  $logInfo = array( "message" => $ex->getMessage(), "code" => $ex->getCode() );
1649  $returnData["code"] = "999";
1650  $returnData["errors"] = $message;
1651  }
1652 
1653  return $returnData;
1654 } // end ACH_RemovePartnerFromTemplate
1655 
1656 /**
1657  * Update the partners in the template list. This is used to update the template partner
1658  * information after an ACH batch submission; It will replace the template information.
1659  *
1660  * NOTE: also update the batch total to match the amounts.
1661  *
1662  * @param array $pDbh -- Current database handle
1663  * @param array $pHBEnv -- Current server environment
1664  * @param array $pTemplateId -- ID of template
1665  * @param array $pPartnerInfo -- The partner information
1666  * @param array $pMC -- The dictionary
1667  *
1668  * @return array -- An array with success code or error information
1669  */
1670 function ACH_UpdateTemplatePartners( $pDbh, $pHBEnv, $pTemplateId, $pPartnerInfo, $pMC ) {
1671  // initialize the return array
1672  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1673 
1674  try {
1675  $SQL = "SELECT tmpl_meta
1676  FROM {$pHBEnv["Cu"]}achtemplate
1677  WHERE tmpl_id = $pTemplateId";
1678 
1679  $rs = db_query( $SQL, $pDbh );
1680 
1681  // get the template information
1682  $templateRow = db_fetch_assoc( $rs, 0 );
1683 
1684  // initialize the partner info
1685  $currentPartnerInfo = array();
1686 
1687  // expand the meta information
1688  $currentMetaInfo = HCU_JsonDecode( $templateRow["tmpl_meta"], true );
1689 
1690  // total up the amounts
1691  $batchTotal = 0;
1692 
1693  for ( $p = 0; $p < count( $pPartnerInfo ); $p++ ) {
1694  $partnerToAdd = array ( "partner" => $pPartnerInfo[$p]["id"],
1695  "notify" => ($pPartnerInfo[$p]["notify"] ? "1" : "0"),
1696  "amount" => $pPartnerInfo[$p]["amount"],
1697  "addenda" => $pPartnerInfo[$p]["addenda"]
1698  );
1699 
1700  $currentPartnerInfo[] = $partnerToAdd;
1701 
1702  // add this partner amount (data should be sanitized by now)
1703  $batchTotal += $pPartnerInfo[$p]["amount"];
1704  }
1705 
1706  // update the template meta data and partner list
1707  $templatePartners = HCU_JsonEncode( $currentPartnerInfo );
1708 
1709  if ( !$templatePartners ) {
1710  throw new Exception( $pMC->msg("ACH Error Encoding", HCU_DISPLAY_AS_HTML) . " 1015", 1015 );
1711  }
1712 
1713  $currentMetaInfo["count"] = count( $currentPartnerInfo );
1714  $currentMetaInfo["total"] = $batchTotal;
1715  $templateMeta = HCU_JsonEncode( $currentMetaInfo );
1716 
1717  if ( !$templateMeta ) {
1718  throw new Exception( $pMC->msg("ACH Error Encoding", HCU_DISPLAY_AS_HTML) . " 1046", 1046 );
1719  }
1720 
1721  $SQL = "UPDATE {$pHBEnv["Cu"]}achtemplate SET tmpl_partner = '$templatePartners', tmpl_meta = '$templateMeta' WHERE tmpl_id = $pTemplateId";
1722  $rs = db_query( $SQL, $pDbh );
1723 
1724  // return success
1725  } catch (Exception $ex) {
1726  $message = $ex->getMessage();
1727  $logInfo = array( "message" => $message, "code" => $ex->getCode() );
1728  $returnData["code"] = "999";
1729  $returnData["errors"] = $message;
1730  }
1731 
1732  return $returnData;
1733 } // end ACH_UpdateTemplatePartners
1734 
1735 
1736 /**
1737  * Process the given CSV file. Look for a partner match based on name. Make three
1738  * resulting lists: "found" for the partners found, "not_found" for when a csv name wasn't
1739  * found in the partner list, and "missed" for any partners in the template that weren't in the CSV file.
1740  *
1741  * @param array $pHBEnv -- Current server environment
1742  * @param array $pTemplateId -- ID of template
1743  * @param array $pCSV -- an array with the CSV file lines
1744  * @param array $pMC -- The dictionary
1745  *
1746  * @return array -- An array with success code and lists, or error information
1747  */
1748 function ACH_ProcessCSV( $pHBEnv, $pTemplateId, $pCSV, $pMC ) {
1749  // initialize the return array
1750  $returnData = array( "code" => "000", "errors" => array(), "data" => array() );
1751 
1752  try {
1753  $dbh = $pHBEnv["dbh"];
1754 
1755  // get the template partners
1756  $SQL = "SELECT tmpl_partner
1757  FROM {$pHBEnv["Cu"]}achtemplate
1758  WHERE tmpl_id = $pTemplateId";
1759 
1760  $rs = db_query( $SQL, $dbh );
1761  if ( !$rs ) {
1762  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1047" );
1763  }
1764 
1765  // get the template information
1766  $templateRow = db_fetch_assoc( $rs, 0 );
1767 
1768  // expand the partner info
1769  $templatePartnerInfo = HCU_JsonDecode( $templateRow["tmpl_partner"], true );
1770 
1771  // get all the partners
1772  $SQL = "SELECT p.id, p.display_name, p.email_notify
1773  FROM {$pHBEnv["Cu"]}user u
1774  INNER JOIN {$pHBEnv["Cu"]}achpartner p ON p.group_id = u.group_id
1775  WHERE u.user_id = {$pHBEnv["Uid"]}";
1776 
1777  $rs = db_query( $SQL, $dbh );
1778  if ( !$rs ) {
1779  throw new Exception( $pMC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1048" );
1780  }
1781 
1782  // make a lookup array
1783  $row = 0;
1784  $partnerLookup = array();
1785  $partnerFound = array(); // this is to find out which template partners were missed
1786  while ( $partnerRow = db_fetch_assoc( $rs, $row++ ) ) {
1787  $partnerLookupByName[$partnerRow["display_name"]] = array( "id" => $partnerRow["id"] , "notify" => $partnerRow["email_notify"] );
1788 
1789  $partnerLookupById[$partnerRow["id"]] = array( "name" => $partnerRow["display_name"] , "notify" => $partnerRow["email_notify"] );
1790  }
1791 
1792  // initialize return arrays
1793  $foundList = array(); // found partners
1794  $notFoundList = array(); // not found partners
1795  $missedList = array(); // in template but not CSV
1796 
1797  // process the CSV file
1798  for ( $i = 0; $i < count( $pCSV ); $i++ ) {
1799  $parts = explode( ",", $pCSV[$i] );
1800  // sanitize
1801  $name = filter_var( $parts[0], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES );
1802  $amount = filter_var( $parts[1], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
1803  if ( isset( $parts[2] ) ) {
1804  $addenda = filter_var( $parts[2], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES );
1805  } else {
1806  unset( $addenda );
1807  }
1808 
1809  // only allow positive amounts
1810  if ( $amount < 0 ) {
1811  $amount = 0;
1812  }
1813 
1814  // see if in partner list
1815  $foundId = isset( $partnerLookupByName[$name] ) ? $partnerLookupByName[$name]["id"] : 0;
1816  if ( $foundId > 0 ) {
1817  // start with the partner setting for the notify
1818  $notify = $partnerLookupByName[$name]["notify"] == "t";
1819 
1820  // get the template partner so can get default values from the template, if necessary
1821  for ( $t = 0; $t < count( $templatePartnerInfo ); $t++ ) {
1822  if ( $templatePartnerInfo[$t]["partner"] == $foundId ) {
1823  // get the notify flag
1824  $notify = $templatePartnerInfo[$t]["notify"] == 1;
1825 
1826  // only get this addenda if not supplied by CSV
1827  if ( !isset( $addenda ) ) {
1828  $addenda = $templatePartnerInfo[$t]["addenda"];
1829  }
1830 
1831  break;
1832  }
1833  }
1834 
1835  if ( !isset( $addenda ) ) { $addenda = ""; }
1836 
1837  // before adding, see if already found this person
1838  $alreadyAdded = false;
1839  for ( $test = 0; $test < count( $foundList ); $test++ ) {
1840  if ( $foundList[$test]["id"] == $foundId ) {
1841  $alreadyAdded = true;
1842  break;
1843  }
1844  }
1845 
1846  if ( !$alreadyAdded ) {
1847  $foundList[] = array( "id" => $foundId, "amount" => $amount, "addenda" => $addenda, "notify" => $notify );
1848  }
1849  } else {
1850  // make sure the name is not already in the list
1851  if ( !in_array( $name, $notFoundList ) ) {
1852  $notFoundList[] = $name;
1853  }
1854  }
1855  }
1856 
1857  // now see which template partners were missed
1858  for ( $t = 0; $t < count( $templatePartnerInfo ); $t++ ) {
1859  $found = false;
1860  for ( $p = 0; $p < count( $foundList ); $p++ ) {
1861  if ( $foundList[$p]["id"] == $templatePartnerInfo[$t]["partner"] ) {
1862  $found = true;
1863  break;
1864  }
1865  }
1866 
1867  if ( !$found ) {
1868  $missedList[] = $partnerLookupById[$templatePartnerInfo[$t]["partner"]]["name"];
1869  }
1870  }
1871 
1872  // these were found and are id, amount, addenda, notify
1873  $returnData["data"]["found"] = $foundList;
1874  // these were not found in partners and are a list of names
1875  $returnData["data"]["not_found"] = $notFoundList;
1876  // these were partners that weren't in the csv and are a list of names
1877  $returnData["data"]["missed"] = $missedList;
1878 
1879  // return success
1880  } catch (Exception $ex) {
1881  $message = $ex->getMessage();
1882  $logInfo = array( "message" => $message, "code" => $ex->getCode() );
1883  $returnData["code"] = "999";
1884  $returnData["errors"] = $message;
1885  }
1886 
1887  return $returnData;
1888 } // end ACH_ProcessCSV
1889 
1890 /**
1891  * ACH_GetCutoffTimeMessage, this function will build a string
1892  * notification message for the user explaining that ach
1893  * transfers will be processed the next business day if the
1894  * transactions is submitted after the credit union cutoff time.
1895  *
1896  * @param $pHBEnv array environment variable
1897  *
1898  * @return string cutoff time message
1899  */
1900 function ACH_GetCutoffTimeMessage($pHBEnv) {
1901  $MC = $pHBEnv['MC'];
1902 
1903  $achCutoffTime = ACH_GetCutoffTime($pHBEnv);
1904  // ACH_GetCutoffTime returns an int value, this will
1905  // remove any leading zeros for time less than 10 am.
1906  // strtotime will not work correctly without the
1907  // leading zero.
1908  if (strlen($achCutoffTime) == 3) {
1909  $achCutoffTime = "0" . $achCutoffTime;
1910  }
1911  $achTime = strtotime($achCutoffTime);
1912  $achTimeFormatted = date("g:i A", $achTime);
1913  $achCutoffMessage = $MC->combo_msg("ACH Cutoff Notice", HCU_DISPLAY_AS_RAW, "#DATE#", $achTimeFormatted);
1914 
1915  return $achCutoffMessage;
1916 }
1917 
1918 
1919 /**
1920  * for the effective date to be correct we must extractthe cutoff date
1921  * specified by the credit union. if there is no default cutoff time,
1922  * we use the default of 3 PM.
1923  *
1924  *
1925  * @param array $pHBEnv -- Current server environment
1926  * @return int $returnCutofftime: -- default is 3 PM if not defined in database
1927  */
1928 function ACH_GetCutoffTime($pHBEnv) {
1929  $returnCutoffTime = null;
1930  $cu = $pHBEnv['Cu'];
1931  $dbh = $pHBEnv['dbh'];
1932  $MC = $pHBEnv['MC'];
1933 
1934  try {
1935  $sql = "
1936  SELECT settings::json->>'cutoff' AS cutoff
1937  FROM cuadmin
1938  WHERE cu='$cu'";
1939  $sqlRs = db_query($sql, $dbh);
1940  if (!$sqlRs) {
1941  throw new Exception( $MC->msg("ACH Query Error", HCU_DISPLAY_AS_HTML) . " 1100" );
1942  }
1943  $sqlData = db_fetch_assoc($sqlRs);
1944 
1945  $returnCutoffTime = $sqlData['cutoff'] !== null ?
1946  intval($sqlData['cutoff']) : 1500;
1947  } catch (Exception $ex) {
1948  // default is 3PM
1949  $returnCutoffTime = 1500;
1950  $logInfo = array( "message" => $message, "code" => $ex->getCode() );
1951  }
1952 
1953  return $returnCutoffTime;
1954 }
1955 
1956 /**
1957  * Get the effective transaction date. This is based on the current credit union time and the CU cutoff time.
1958  * Also take into account any defined business holidays.
1959  *
1960  *
1961  * @param array $pHBEnv -- Current server environment
1962  * @param strng $pTestOverrideDateTime -- Optional date/time to force a certain test date/time (parsable by strtotime())
1963  *
1964  * @return string -- A string with a date in it (m/d/Y)
1965  */
1966 function ACH_GetEffectiveDate( $pHBEnv, $pTestOverrideDateTime = "" ) {
1967  $returnEffDate = null;
1968 
1969  try {
1970  // get today as current effective date
1971  $effDate = new DateTime();
1972  $effDate->setTimezone(new DateTimeZone( $pHBEnv["tz"] ));
1973  $effDateFound = false;
1974 
1975  if ($pTestOverrideDateTime > "") {
1976  // this path is just for test injection
1977  $effDate->setDate(
1978  date( "Y", strtotime( $pTestOverrideDateTime) ),
1979  date( "m", strtotime( $pTestOverrideDateTime) ),
1980  date( "d", strtotime( $pTestOverrideDateTime) )
1981  );
1982 
1983  $effDate->setTime(
1984  date( "G", strtotime( $pTestOverrideDateTime) ),
1985  date( "i", strtotime( $pTestOverrideDateTime) )
1986  );
1987  }
1988 
1989  // get cut off time - evetually read from database
1990  // get this years business holidays
1991  $cuCutoffTime = ACH_GetCutoffTime($pHBEnv);
1992  $cuCalendar = ACH_GetCalendarYear($pHBEnv, $effDate->format("Y"));
1993  $cuCalendar = $cuCalendar['calendar'];
1994  $cuCalendarDates = $cuCalendar['dates'];
1995  $cuCalendarDates = HCU_JsonDecode($cuCalendarDates);
1996 
1997  // check cutoff time
1998  // only need to check cutoff time for today,
1999  // initial effective date is set to today.
2000  $effTime = (int) $effDate->format("Gi");
2001  if ($effTime > $cuCutoffTime) {
2002  $effDate->modify("+1 day");
2003  }
2004 
2005  // loop to check if dates are weekend or
2006  // selected as non-business days
2007  while (!$effDateFound) {
2008  // get effective date: weekeday 1-7
2009  $effWeekday = (int) $effDate->format("N");
2010 
2011  // check: saturday or sunday
2012  // incriment to next day and re-run loop
2013  if ($effWeekday >= 6) {
2014  $effDate->modify("+1 day");
2015  continue;
2016  }
2017 
2018  // the calendar data has an array of dates
2019  // this means any dates found in this array are CLOSED for business
2020  // or scheduled as a business holiday.
2021 
2022  // check: array exists
2023  // check: current effective date is in array
2024  // date found, incriment to next day, re-run loop.
2025  if (is_array($cuCalendarDates)) {
2026  if (in_array($effDate->format("Y-m-d"), $cuCalendarDates)){
2027  $effDate->modify("+1 day");
2028  continue;
2029  }
2030  }
2031 
2032  // if this far, a valid effective date has been found
2033  $effDateFound = true;
2034  }
2035 
2036  $returnEffDate = $effDate->format("m/d/Y");
2037  } catch ( Exception $e ) {
2038  // just return today and let the back-end catch it
2039  $returnEffDate = date( "m/d/Y" );
2040  }
2041 
2042  return $returnEffDate;
2043 } // end ACH_GetEffectiveDate
2044 
2045 function ACH_GetCalendarYear($pHBEnv, $pYear) {
2046  $dbh = $pHBEnv['dbh'];
2047  $cu = $pHBEnv['Cu'];
2048 
2049  $sqlReturn = array();
2050  $sql = "
2051  SELECT dates FROM cu_calendar
2052  WHERE cu = '$cu'
2053  AND year = '$pYear'";
2054  $sqlRs = db_query($sql, $dbh);
2055  if (!$sqlRs) {
2056  throw new Exception("Failed to read calendar for " . $pYear);
2057  }
2058  $sqlReturn['calendar'] = db_fetch_assoc($sqlRs);
2059  return $sqlReturn;
2060 }
2061 
2062 /**
2063  * Test to see if the provided date is the effective transaction date. Return the date
2064  * if it is a business day, or the next business day if it is not.
2065  * Take into account any defined business holidays.
2066  *
2067  *
2068  * @param array $pEnv -- Current server environment
2069  * @param strng $pEffDate -- Date/time to test (parsable by strtotime())
2070  *
2071  * @return string -- A string with a date in it (Y-m-d)
2072  */
2073 function ACH_EnsureBusinessDay( $pEnv, $pEffDate ) {
2074  $returnEffDate = null;
2075 
2076  try {
2077  // make an object to test/manipulate easier
2078  $effDate = new DateTime( $pEffDate );
2079 
2080  // get this years business holidays
2081  $cuCalendar = ACH_GetCalendarYear($pEnv, $effDate->format("Y"));
2082  $cuCalendar = $cuCalendar['calendar'];
2083  $cuCalendarDates = $cuCalendar['dates'];
2084  $cuCalendarDates = HCU_JsonDecode($cuCalendarDates);
2085 
2086  // loop to check if dates are weekend or
2087  // selected as non-business days
2088  $effDateFound = false;
2089  while (!$effDateFound) {
2090  // get effective date: weekeday 1-7
2091  $effWeekday = (int) $effDate->format("N");
2092 
2093  // check: saturday or sunday
2094  // increment to next day and re-run loop
2095  if ($effWeekday >= 6) {
2096  $effDate->modify("+1 day");
2097  continue;
2098  }
2099 
2100  // the calendar data has an array of dates
2101  // this means any dates found in this array are CLOSED for business
2102  // or scheduled as a business holiday.
2103 
2104  // check: array exists
2105  // check: current effective date is in array
2106  // date found, incriment to next day, re-run loop.
2107  if (is_array($cuCalendarDates)) {
2108  if (in_array($effDate->format("Y-m-d"), $cuCalendarDates)){
2109  $effDate->modify("+1 day");
2110  continue;
2111  }
2112  }
2113 
2114  // if this far, a valid effective date has been found
2115  $effDateFound = true;
2116  }
2117 
2118  $returnEffDate = $effDate->format("Y-m-d");
2119  } catch ( Exception $e ) {
2120  // just return today and let the back-end catch it
2121  $returnEffDate = $pEffDate;
2122  }
2123 
2124  return $returnEffDate;
2125 } // end ACH_EnsureBusinessDay
2126 
2127 
2128 // Return the list of states to use for a State drop-down list.
2129 // NOTE: this is formatted for use with a Kendo DropdownList.
2130 function GetStateList() {
2131  $states = array(
2132  [ "name"=>'Alabama', "value"=>'AL'],
2133  [ "name"=>'Alaska', "value"=>'AK'],
2134  [ "name"=>'Arizona', "value"=>'AZ'],
2135  [ "name"=>'Arkansas', "value"=>'AR'],
2136  [ "name"=>'California', "value"=>'CA'],
2137  [ "name"=>'Colorado', "value"=>'CO'],
2138  [ "name"=>'Connecticut', "value"=>'CT'],
2139  [ "name"=>'Delaware', "value"=>'DE'],
2140  [ "name"=>'Florida', "value"=>'FL'],
2141  [ "name"=>'Georgia', "value"=>'GA'],
2142  [ "name"=>'Hawaii', "value"=>'HI'],
2143  [ "name"=>'Idaho', "value"=>'ID'],
2144  [ "name"=>'Illinois', "value"=>'IL'],
2145  [ "name"=>'Indiana', "value"=>'IN'],
2146  [ "name"=>'Iowa', "value"=>'IA'],
2147  [ "name"=>'Kansas', "value"=>'KS'],
2148  [ "name"=>'Kentucky', "value"=>'KY'],
2149  [ "name"=>'Louisiana', "value"=>'LA'],
2150  [ "name"=>'Maine', "value"=>'ME'],
2151  [ "name"=>'Maryland', "value"=>'MD'],
2152  [ "name"=>'Massachusetts', "value"=>'MA'],
2153  [ "name"=>'Michigan', "value"=>'MI'],
2154  [ "name"=>'Minnesota', "value"=>'MN'],
2155  [ "name"=>'Mississippi', "value"=>'MS'],
2156  [ "name"=>'Missouri', "value"=>'MO'],
2157  [ "name"=>'Montana', "value"=>'MT'],
2158  [ "name"=>'Nebraska', "value"=>'NE'],
2159  [ "name"=>'Nevada', "value"=>'NV'],
2160  [ "name"=>'New Hampshire', "value"=>'NH'],
2161  [ "name"=>'New Jersey', "value"=>'NJ'],
2162  [ "name"=>'New Mexico', "value"=>'NM'],
2163  [ "name"=>'New York', "value"=>'NY'],
2164  [ "name"=>'North Carolina', "value"=>'NC'],
2165  [ "name"=>'North Dakota', "value"=>'ND'],
2166  [ "name"=>'Ohio', "value"=>'OH'],
2167  [ "name"=>'Oklahoma', "value"=>'OK'],
2168  [ "name"=>'Oregon', "value"=>'OR'],
2169  [ "name"=>'Pennsylvania', "value"=>'PA'],
2170  [ "name"=>'Rhode Island', "value"=>'RI'],
2171  [ "name"=>'South Carolina', "value"=>'SC'],
2172  [ "name"=>'South Dakota', "value"=>'SD'],
2173  [ "name"=>'Tennessee', "value"=>'TN'],
2174  [ "name"=>'Texas', "value"=>'TX'],
2175  [ "name"=>'Utah', "value"=>'UT'],
2176  [ "name"=>'Vermont', "value"=>'VT'],
2177  [ "name"=>'Virginia', "value"=>'VA'],
2178  [ "name"=>'Washington', "value"=>'WA'],
2179  [ "name"=>'West Virginia', "value"=>'WV'],
2180  [ "name"=>'Wisconsin', "value"=>'WI'],
2181  [ "name"=>'Wyoming', "value"=>'WY']
2182  );
2183 
2184  $provinces = array(
2185  [ "name"=>'Alberta', "value" => 'AB' ],
2186  [ "name"=>'British Columbia', "value" => 'BC' ],
2187  [ "name"=>'Manitoba', "value" => 'MB' ],
2188  [ "name"=>'New Brunswick', "value" => 'NB' ],
2189  [ "name"=>'Newfoundland and Labrador', "value" => 'NL' ],
2190  [ "name"=>'Nova Scotia', "value" => 'NS' ],
2191  [ "name"=>'Ontario', "value" => 'ON' ],
2192  [ "name"=>'Prince Edward Island', "value" => 'PE' ],
2193  [ "name"=>'Saskatchewan', "value" => 'SK' ],
2194  [ "name"=>'Quebec', "value" => 'QC' ]
2195  );
2196 
2197  $caribbean = array(
2198  [ "name"=>'US Virgin Islands', "value" => 'VI' ],
2199  [ "name"=>'Trinidad and Tobago', "value" => 'TT' ]
2200  );
2201  // put them together and return them
2202  $returnData = array_merge( $states, $provinces, $caribbean );
2203 
2204  return $returnData;
2205 } // end GetStateList
2206 
2207 /**
2208  * ACH_GetCompanyData
2209  * This function will retrieve the group name and tax_id
2210  * by user_id, to check access rights for commercial banking
2211  * features and display to company name to the user.
2212  *
2213  * If no tax id is found the user will have no access to
2214  * commercial banking features.
2215  *
2216  * @param $pEnv array environment values
2217  * @return $sqlData array group information found
2218  */
2219 function ACH_GetCompanyData($pEnv) {
2220  // get group information to retrieve group/company name
2221  $sql = "
2222  SELECT g.group_name, g.tax_id FROM {$pEnv['cu']}user u
2223  LEFT JOIN {$pEnv['cu']}group g ON u.group_id = g.group_id
2224  WHERE u.user_id = {$pEnv['Uid']}";
2225  $sqlRs = db_query($sql, $pEnv['dbh']);
2226  if (!$sqlRs) {
2227  throw new Exception("Failed to read company information");
2228  }
2229 
2230  $sqlData = db_fetch_assoc($sqlRs);
2231  return $sqlData;
2232 }
2233 
2234 function PrintPartnerEditor($pEnv) {?>
2235  <?php $MC = $pEnv['MC']; ?>
2236  <!-- ACCORDION STYLES -->
2237  <style type="text/css">
2238  .achAccordionHeader {
2239  cursor: pointer;
2240  }
2241 
2242  .achFieldMargin {
2243  margin-top: 7.5px;
2244  margin-bottom: 7.5px;
2245  }
2246 
2247  .achSpacer {
2248  background-color: #ddd;
2249  height: 1px;
2250  }
2251 
2252  .achDirty {
2253  color: #f0ad4e;
2254  }
2255  </style>
2256 
2257  <script type="text/x-kendo-template" id="achPartnerEditorTmp">
2258  <div id="achPartnerEditor">
2259  <div id="achPartnerStatus"></div>
2260  <div class="col-sm-12">&nbsp;</div>
2261  <div class="hcu-secondary"
2262  data-bind="visible: showDeleteWarn">
2263  <span class="small hcu-secondary-text"><?php echo $MC->msg("ACH Remove Partner from batch warning", HCU_DISPLAY_AS_HTML) ?></span>
2264  </div>
2265  <div class="well well-sm col-sm-12">
2266  <!-- PARTNER INFORMATION -->
2267  <div class="row">
2268  <div class="col-xs-12">
2269  <h4>
2270  <span><?php echo $MC->msg("ACH Partner Information", HCU_DISPLAY_AS_HTML) ?></span>
2271  <span class="achDirty" data-bind="visible: sourceDirtyInfo"><sup>*</sup></span>
2272  </h4>
2273  <br>
2274  </div>
2275  </div>
2276 
2277  <div class="row achFieldMargin">
2278  <div class="col-xs-12 col-sm-3">
2279  <label for="achPartnerType">
2280  <span><?php echo $MC->msg("ACH Partner Type", HCU_DISPLAY_AS_HTML) ?></span>
2281  </label>
2282  </div>
2283  <div class="col-xs-12 col-sm-6">
2284  <input type="text" name="achPartnerType" class="hcu-all-100"
2285  data-role="dropdownlist"
2286  data-text-field="display"
2287  data-value-field="type"
2288  data-bind="
2289  source: listPartnerTypes,
2290  value: sourcePartner.partner_type,
2291  events: { change: change }">
2292  </div>
2293  </div>
2294 
2295  <div class="row achFieldMargin">
2296  <div class="col-xs-12 col-sm-3">
2297  <label for="achPartnerDisplay">
2298  <span><?php echo $MC->msg("ACH Display Name", HCU_DISPLAY_AS_HTML) ?> </span>
2299  <span class="hcu-required-field"><sup>*</sup></span></span>
2300  </label>
2301  </div>
2302  <div class="col-xs-12 col-sm-6">
2303  <input type="text" name="achPartnerDisplay" class="k-textbox k-input hcu-all-100"
2304  maxlength="30"
2305  data-bind="
2306  value: sourcePartner.display_name,
2307  events: { change: change }"
2308  validationMessage="
2309  <?php echo $MC->msg("ACH Unique display name required", HCU_DISPLAY_AS_HTML) ?>"
2310  required>
2311  </div>
2312  </div>
2313 
2314  <div class="row achFieldMargin">
2315  <div class="col-xs-12 col-sm-3">
2316  <label for="achPartnerName">
2317  <span><?php echo $MC->msg("Name", HCU_DISPLAY_AS_HTML); ?> </span>
2318  </label>
2319  </div>
2320  <div class="col-xs-12 col-sm-6">
2321  <input type="text" name="achPartnerName" class="k-textbox k-input hcu-all-100"
2322  maxlength="30"
2323  data-bind="
2324  value: sourcePartner.ach_name,
2325  events: { change: change }">
2326  </div>
2327  </div>
2328 
2329  <div class="row achFieldMargin">
2330  <div class="col-xs-12 col-sm-3">
2331  <label for="achPartnerEmail">
2332  <span><?php echo $MC->msg("ACH E-Mail", HCU_DISPLAY_AS_HTML); ?> </span>
2333  <span class="hcu-required-field"
2334  data-bind="visible: sourcePartner.email_notify"><sup>*</sup></span></span>
2335  </label>
2336  </div>
2337  <div class="col-xs-12 col-sm-6">
2338  <input type="TEXT" name="achPartnerEmail" class="k-textbox k-input hcu-all-100"
2339  maxlength="100"
2340  homecu-match="email"
2341  data-bind="
2342  value: sourcePartner.email,
2343  required: sourcePartner.email_notify,
2344  events: { change: change }"
2345  data-required-msg="<?php echo $MC->msg("Please specify email address", HCU_DISPLAY_AS_HTML); ?>"
2346  validationMessage="<?php echo $MC->msg("Enter Valid Email", HCU_DISPLAY_AS_HTML); ?>">
2347  </div>
2348  <div class="col-xs-12 col-sm-3">
2349  <input type="checkbox" name="achPartnerNotify" style="margin-top: -2px;"
2350  data-bind="
2351  checked: sourcePartner.email_notify,
2352  events: { click: change }">
2353  <label for="achPartnerNotify"
2354  data-bind="
2355  events: { click: change }">
2356  <span>&nbsp;<?php echo $MC->msg("Notify", HCU_DISPLAY_AS_HTML); ?>&nbsp;<span class="fa fa-question-circle-o" id="achPartnerNotifyTip"></span></span>
2357  </label>
2358  </div>
2359  </div>
2360 
2361  <div class="achFieldMargin achSpacer"></div>
2362 
2363  <div class="row">
2364  <div class="col-xs-12">
2365  <h4>
2366  <span><?php echo $MC->msg("ACH Partner Address", HCU_DISPLAY_AS_HTML); ?></span>
2367  <span class="achDirty" data-bind="visible: sourceDirtyAddr"><sup>*</sup></span>
2368  </h4>
2369  <br>
2370  </div>
2371  </div>
2372 
2373  <div class="row achFieldMargin">
2374  <div class="col-xs-12 col-sm-3">
2375  <label for="achPartnerAddress1">
2376  <span><?php echo $MC->msg("ACH Address", HCU_DISPLAY_AS_HTML) . " 1"; ?></span>
2377  </label>
2378  </div>
2379  <div class="col-xs-12 col-sm-6">
2380  <input type="text" name="achPartnerAddress1" class="k-textbox k-input hcu-all-100"
2381  maxlength="35"
2382  data-bind="
2383  value: sourcePartner.address1,
2384  events: { change: change }">
2385  </div>
2386  </div>
2387 
2388  <div class="row achFieldMargin">
2389  <div class="col-xs-12 col-sm-3">
2390  <label for="achPartnerAddress2">
2391  <span><?php echo $MC->msg("ACH Address", HCU_DISPLAY_AS_HTML) . " 2"; ?></span>
2392  </label>
2393  </div>
2394  <div class="col-xs-12 col-sm-6">
2395  <input type="text" name="achPartnerAddress2" class="k-textbox k-input hcu-all-100"
2396  maxlength="30"
2397  data-bind="
2398  value: sourcePartner.address2,
2399  events: { change: change }">
2400  </div>
2401  </div>
2402 
2403  <div class="row achFieldMargin">
2404  <div class="col-xs-12 col-sm-3">
2405  <label>
2406  <span><?php echo $MC->msg("ACH CityStateZip", HCU_DISPLAY_AS_HTML); ?></span>
2407  </label>
2408  </div>
2409  <div class="col-xs-12 col-sm-3">
2410  <input type="text" name="achPartnerCity" class="k-textbox k-input hcu-all-100"
2411  maxlength="20"
2412  data-bind="
2413  value: sourcePartner.city,
2414  events: { change: change }">
2415  </div>
2416  <div class="col-xs-12 col-sm-3">
2417  <input type="text" name="achPartnerState" class="hcu-all-100"
2418  maxlength="20"
2419  data-role="dropdownlist"
2420  data-text-field="name"
2421  data-value-field="value"
2422  data-option-label="<?php echo $MC->msg("ACH Select State", HCU_DISPLAY_AS_JS) ?>"
2423  data-bind="
2424  source: listStates,
2425  value: sourcePartner.state,
2426  events: { change: change }">
2427  </div>
2428  <div class="col-xs-12 col-sm-3">
2429  <input type="text" name="achPartnerZip" class="k-textbox k-input hcu-all-100"
2430  data-role="maskedtextbox"
2431  data-bind="
2432  value: sourcePartner.zip,
2433  events: { change: change }">
2434  </div>
2435  </div>
2436 
2437  <div class="row achFieldMargin">
2438  <div class="col-xs-12 col-sm-3">
2439  <label for="achPartnerCountry">
2440  <span><?php echo $MC->msg("ACH Country", HCU_DISPLAY_AS_HTML); ?></span>
2441  </label>
2442  </div>
2443  <div class="col-xs-12 col-sm-6">
2444  <input type="text" name="achPartnerCountry" class="k-textbox k-input hcu-all-100"
2445  maxlength="100"
2446  data-bind="
2447  value: sourcePartner.country,
2448  events: { change: change }">
2449  </div>
2450  </div>
2451 
2452  <div class="achFieldMargin achSpacer"></div>
2453 
2454  <div class="row">
2455  <div class="col-xs-12">
2456  <h4>
2457  <span><?php echo $MC->msg("ACH Remote Account", HCU_DISPLAY_AS_HTML); ?></span>
2458  <span class="achDirty" data-bind="visible: sourceDirtyAcct"><sup>*</sup></span>
2459  </h4>
2460  <br>
2461  </div>
2462  </div>
2463  <div class="row achFieldMargin">
2464  <div class="col-xs-12 col-sm-3">
2465  <label for="achPartnerRouting">
2466  <span><?php echo $MC->msg("ACH Routing Number", HCU_DISPLAY_AS_HTML); ?></span>
2467  <span class="hcu-required-field"><sup>*</sup></span></span>
2468  </label>
2469  </div>
2470  <div class="col-xs-12 col-sm-6">
2471  <input type="text" name="achPartnerRouting" class="k-textbox k-input hcu-all-100"
2472  data-bind="
2473  value: sourcePartner.dfi_routing,
2474  events: { change: change }"
2475  data-required-msg="Routing number is required"
2476  validationMessage="<?php echo $MC->msg("ACH Routing number 9 digits", HCU_DISPLAY_AS_HTML); ?>"
2477  required>
2478  </div>
2479  </div>
2480 
2481  <div class="row achFieldMargin">
2482  <div class="col-xs-12 col-sm-3">
2483  <label for="achPartnerAccount">
2484  <span><?php echo $MC->msg('ACH Account Number', HCU_DISPLAY_AS_HTML); ?> </span>
2485  <span class="hcu-required-field"><sup>*</sup></span></span>
2486  </label>
2487  </div>
2488  <div class="col-xs-12 col-sm-6">
2489  <input type="text" name="achPartnerAccount" class="k-textbox k-input hcu-all-100 account-match"
2490  data-bind="
2491  value: sourcePartner.dfi_account,
2492  events: { change: change }"
2493  data-required-msg="<?php echo $MC->msg('ACH Need remote account', HCU_DISPLAY_AS_HTML); ?>"
2494  validationMessage="
2495  <?php echo $MC->msg('ACH Need remote account', HCU_DISPLAY_AS_HTML); ?>"
2496  required>
2497  </div>
2498  </div>
2499 
2500  <div class="row achFieldMargin">
2501  <div class="col-xs-12 col-sm-3">
2502  <label for="achPartnerAccountConfirm">
2503  <span><?php echo $MC->msg('ACH Confirm Account', HCU_DISPLAY_AS_HTML); ?> </span>
2504  <span class="hcu-required-field"><sup>*</sup></span></span>
2505  </label>
2506  </div>
2507  <div class="col-xs-12 col-sm-6">
2508  <input type="text" name="achPartnerAccountConfirm" class="k-textbox k-input hcu-all-100 account-match"
2509  homecu-equals="account-match"
2510  data-bind="
2511  value: sourcePartner.dfi_account_confirm,
2512  events: { change: change }"
2513  data-required-msg="<?php echo $MC->msg('ACH Need remote account', HCU_DISPLAY_AS_HTML); ?>"
2514  validationMessage="
2515  <?php echo $MC->msg('ACH Accounts no match', HCU_DISPLAY_AS_HTML); ?>"
2516  required>
2517  </div>
2518  </div>
2519 
2520  <div class="row achFieldMargin">
2521  <div class="col-xs-12 col-sm-3">
2522  <label for="achPartnerAccountType">
2523  <span><?php echo $MC->msg('Account Type', HCU_DISPLAY_AS_HTML); ?> </span>
2524  <span class="hcu-required-field"><sup>*</sup></span></span>
2525  </label>
2526  </div>
2527  <div class="col-xs-12 col-sm-6">
2528  <input name="achPartnerAccountType" class="hcu-all-100"
2529  data-role="dropdownlist"
2530  data-text-field="display"
2531  data-value-field="type"
2532  data-option-label="<?php echo $MC->msg("ACH Select Type", HCU_DISPLAY_AS_JS) ?>"
2533  data-bind="
2534  source: listAccountTypes,
2535  value: sourcePartner.dfi_account_type,
2536  events: { change: change }"
2537  data-required-msg="
2538  <?php echo $MC->msg('ACH Need remote account type', HCU_DISPLAY_AS_HTML) ?>"
2539  validationMessage=""
2540  required>
2541  </div>
2542  </div>
2543 
2544  <div class="achFieldMargin achSpacer"></div>
2545 
2546  <div class="achFieldMargin">
2547  &nbsp;
2548  <span class="hcu-required-field"><sup>*</sup></span>
2549  <span><?php echo $MC->msg('ACH Required', HCU_DISPLAY_AS_HTML) ?></span>
2550  </div>
2551 
2552  </div>
2553 
2554  <div class="hcu-template">
2555  <div class="hcu-edit-buttons k-state-default">
2556  <span class="hcu-icon-delete"
2557  data-bind="visible: showDelete">
2558  <a href="##" id="lnkDelete"
2559  data-bind="events: { click: delete }">
2560  <i class="fa fa-trash fa-lg"></i>
2561  </a>
2562  </span>
2563  <a href="##" id="lnkCancel"
2564  data-bind="events: { click: cancel }">
2565  <?php echo $MC->msg('Cancel', HCU_DISPLAY_AS_HTML) ?></a>
2566  &ensp;
2567  <a href="##" id="btnUpdate" class="k-button k-primary"
2568  data-bind="events: { click: save }">
2569  <i class="fa fa-check fa-lg"></i>
2570  <?php echo $MC->msg('Save', HCU_DISPLAY_AS_HTML) ?>
2571  </a>
2572  </div>
2573  </div>
2574  </div>
2575  </script>
2576 
2577  <script type="text/x-kendo-template" id=achPartnerDiscardTmp>
2578  <div id="achPartnerDiscard">
2579  <p><?php echo $MC->msg("ACH Discard changes warning", HCU_DISPLAY_AS_JS) ?></p>
2580  <p><?php echo $MC->msg("ACH Continue", HCU_DISPLAY_AS_JS) ?></p>
2581  </div>
2582  </script>
2583 
2584  <script type="text/x-kendo-template" id="achPartnerDeleteTmp">
2585  <div id="achPartnerDelete">
2586  <p><?php echo $MC->msg("ACH Continue remove partner", HCU_DISPLAY_AS_JS) ?></p>
2587  <p><?php echo $MC->msg("ACH Continue", HCU_DISPLAY_AS_JS) ?></p>
2588  </div>
2589  </script>
2590  <?php
2591 }