From ba79bfd716e1a7f30cdfca10e5df8a70d9aafa48 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 22 Oct 2018 16:02:20 +0100 Subject: [PATCH 01/68] bump version and require CF v1.7.3 --- caldera-forms-civicrm.php | 6 +++--- readme.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caldera-forms-civicrm.php b/caldera-forms-civicrm.php index e5f3195..572fc7b 100644 --- a/caldera-forms-civicrm.php +++ b/caldera-forms-civicrm.php @@ -2,7 +2,7 @@ /** * Plugin Name: Caldera Forms CiviCRM * Description: CiviCRM integration for Caldera Forms. - * Version: 0.4.4 + * Version: 1.0 * Author: Andrei Mondoc * Author URI: https://github.com/mecachisenros * Plugin URI: https://github.com/mecachisenros/caldera-forms-civicrm @@ -16,7 +16,7 @@ * * @since 0.1 */ -define( 'CF_CIVICRM_INTEGRATION_VER', '0.4.4' ); +define( 'CF_CIVICRM_INTEGRATION_VER', '1.0' ); define( 'CF_CIVICRM_INTEGRATION_URL', plugin_dir_url( __FILE__ ) ); define( 'CF_CIVICRM_INTEGRATION_PATH', plugin_dir_path( __FILE__ ) ); @@ -183,7 +183,7 @@ public static function instance() { private function check_dependencies() { // Bail if Caldera Forms is not available - if ( ! defined( 'CFCORE_VER' ) || ! version_compare( CFCORE_VER, '1.7', '>=' ) ) { + if ( ! defined( 'CFCORE_VER' ) || ! version_compare( CFCORE_VER, '1.7.3', '>=' ) ) { add_action( 'admin_notices', [$this, 'caldera_forms_version_notice'] ); return false; } diff --git a/readme.txt b/readme.txt index 12bdb8d..ca43f2a 100755 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: mecachisenros, needle Tags: civicrm, caldera, forms, integration Requires at least: 4.5 -Tested up to: 4.9.4 -Stable tag: 0.4.3 +Tested up to: 4.9.8 +Stable tag: 1.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -16,7 +16,7 @@ The Caldera Forms CiviCRM plugin contains a set of form processors that interact ### Requirements -This plugin requires a minimum of *CiviCRM 4.6* and *Caldera Forms 1.4.2*. +This plugin requires a minimum of *CiviCRM 4.6* and *Caldera Forms 1.7.3*. ### Plugin Development From 70fdf0cc6293a4bbe5b09438a1d2e47c330d54fb Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 5 Nov 2018 13:00:23 +0000 Subject: [PATCH 02/68] participant processor, and some minor improvements --- .../class-civicrm-price-sets-presets.php | 4 +- .../class-civicrm-caldera-forms-forms.php | 137 ++-- .../class-civicrm-caldera-forms-helper.php | 101 ++- ...class-civicrm-caldera-forms-processors.php | 5 +- .../line-item/class-line-item-processor.php | 49 +- .../membership/class-membership-processor.php | 23 +- processors/order/class-order-processor.php | 10 +- .../class-participant-processor.php | 618 ++++++++++++++++++ processors/participant/config.php | 163 +++++ templates/notice.php | 5 + templates/notices.php | 25 + {template => templates}/thank-you.php | 0 12 files changed, 1051 insertions(+), 89 deletions(-) create mode 100644 processors/participant/config.php create mode 100644 templates/notice.php create mode 100644 templates/notices.php rename {template => templates}/thank-you.php (100%) diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index 18296de..e37b9b8 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -48,7 +48,7 @@ public function register_hooks() { // auto-populate Price Fields add_action( 'caldera_forms_autopopulate_types', [ $this, 'autopopulate_price_field_types' ] ); - add_filter( 'caldera_forms_render_get_field', [ $this, 'autopopulate_price_field_values' ], 20, 2 ); + add_filter( 'caldera_forms_render_get_field', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); } @@ -118,7 +118,7 @@ public function autopopulate_price_field_values( $field, $form ) { foreach ( $this->price_sets as $price_set_id => $price_set ) { foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { if( $field['config']['auto_type'] == 'cfc_price_field_' . $price_field_id ) { - foreach ( $price_field['price_field_values'] as $value_id => $price_field_value) { + foreach ( $price_field['price_field_values'] as $value_id => $price_field_value ) { $field['config']['option'][$value_id] = [ 'value' => $value_id, 'label' => $price_field_value['label'] . ' - ' . $field['config']['price_field_currency'] . ' ' . $price_field_value['amount'], diff --git a/includes/class-civicrm-caldera-forms-forms.php b/includes/class-civicrm-caldera-forms-forms.php index ade46d6..26f7eaa 100644 --- a/includes/class-civicrm-caldera-forms-forms.php +++ b/includes/class-civicrm-caldera-forms-forms.php @@ -53,7 +53,7 @@ public function register_hooks() { // form render transient add_filter( 'caldera_forms_render_get_form', [ $this, 'set_form_transient' ], 1 ); - add_action( 'caldera_forms_render_end', [ $this, 'delete_form_transient' ] ); + add_action( 'caldera_forms_render_end', [ $this, 'delete_form_transient' ], 1 ); // form submission transient add_filter( 'caldera_forms_submit_get_form', [ $this, 'set_form_transient' ] ); @@ -67,6 +67,8 @@ public function register_hooks() { add_filter( 'caldera_forms_magic_summary_should_use_label', [ $this, 'summary_use_label' ], 10, 3 ); // exclude hidden fields from summary add_filter( 'caldera_forms_summary_magic_fields', [ $this, 'exclude_hidden_fields_in_summary' ], 10, 2 ); + // render notices template + add_action( 'caldera_forms_render_start', [ $this, 'render_notices_template' ] ); } @@ -83,13 +85,9 @@ public function set_form_transient( $form ) { // bail if no processors if ( empty( $form['processors'] ) ) return $form; - $has_contact_processor = false; - if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) - $has_contact_processor = true; - - // set transient structure - if ( $has_contact_processor ) $this->set_transient_structure( $form ); + // set transient structure + $this->set_transient_structure( $form ); return $form; } @@ -115,43 +113,61 @@ public function delete_form_transient() { public function set_transient_structure( $form ) { $structure = ( object ) []; + $structure->contacts = ( object ) []; + $structure->memberships = ( object ) []; + $structure->participants = ( object ) []; + $structure->events = ( object ) []; + $structure->line_items = ( object ) []; + $structure->orders = ( object ) []; - foreach ( $form['processors'] as $id => $processor ) { - if ( isset( $processor['runtimes'] ) ) { - if ( $processor['type'] == 'civicrm_contact' ) { - $structure->contacts = new stdClass(); - $structure->contacts->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_membership' ) { - $structure->memberships = new stdClass(); - $structure->memberships->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_participant' ) { - $structure->participants = new stdClass(); - $structure->participants->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_order' ) { - $structure->orders = new stdClass(); - $structure->orders->$id = new stdClass(); - } + array_map( function( $id, $processor ) use ( $structure, $form ) { - if ( $processor['type'] == 'civicrm_line_item' ) { - $structure->line_items = new stdClass(); - $structure->line_items->$id = new stdClass(); + if ( ! isset( $processor['runtimes'] ) ) return; + // contacts + if ( $processor['type'] == 'civicrm_contact' ) { + $structure->contacts->$id = ( object ) []; + // get current logged in/checksum contact if any + $contact = $this->plugin->helper->current_contact_data_get(); + if ( $contact ) { + // FIXME + // revise use of 'processor_id' or 'cid_x' + $structure->contacts->$id = $contact['contact_id']; + $structure->contacts->{'cid_'.$processor['config']['contact_link']} = $contact['contact_id']; } - + return; + } + // memberships + if ( $processor['type'] == 'civicrm_membership' ) { + $structure->memberships->$id = ( object ) []; + return; + } + // participants and events + if ( $processor['type'] == 'civicrm_participant' ) { + $structure->participants->$id = ( object ) []; + // add events and corresponding event_id + $structure->events->$id = ( object ) []; + $structure->events->$id->event_id = $form['processors'][$id]['config']['id']; + return; + } + // line items + if ( $processor['type'] == 'civicrm_line_item' ) { + $structure->line_items->$id = ( object ) []; + return; } - } + // orders + if ( $processor['type'] == 'civicrm_order' ) { + $structure->orders->$id = ( object ) []; + return; + } + + }, array_keys( $form['processors'] ), $form['processors'] ); /** * Transient structure, fires at form subsmission and at render time. * * @since 0.4.4 * - * @param object $transient The transient stricture + * @param object $structure The transient structure * @param array $form Form config */ apply_filters( 'cfc_filter_transient_structure', $structure, $form ); @@ -161,6 +177,24 @@ public function set_transient_structure( $form ) { return $form; } + /** + * Render notices template at the top of the form. + * @since 1.0 + * @param array $form Form config + */ + public function render_notices_template( $form ) { + /** + * Filter to replace the notices template. + * @since 1.0 + * @var string $template_path The notices template path + */ + $template_path = apply_filters( 'cfc_notices_template_path', CF_CIVICRM_INTEGRATION_PATH . 'templates/notices.php', $form ); + + $html = $this->plugin->html->generate( $form, $template_path ); + // output template + echo $html; + } + /** * Add CiviCRM panel. * @@ -192,7 +226,7 @@ public function add_civicrm_tab( $panels ) { */ public function summary_use_label( $use, $field, $form ) { - if( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) + if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) return true; return $use; @@ -207,13 +241,12 @@ public function summary_use_label( $use, $field, $form ) { * @param array $form Form config */ public function exclude_hidden_fields_in_summary( $fields, $form ) { - if( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) { - foreach ( $fields as $id => $field ) { - if ( $field['type'] == 'hidden' ) { - unset( $fields[$id] ); - } - } - } + + if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) + return array_filter( $fields, function( $field ) { + return $field['type'] !== 'hidden'; + } ); + return $fields; } @@ -230,22 +263,16 @@ public function reorder_contact_processors( $form ) { // continue as normal if form has no processors if ( empty( $form['processors'] ) ) return $form; - $contact_processors = $rest_processors = []; - foreach ( $form['processors'] as $pId => $processor ) { - if ( $processor['type'] == 'civicrm_contact' ) { - $contact_processors[$pId] = $processor; - } - if ( $processor['type'] != 'civicrm_contact' ) { - $rest_processors[$pId] = $processor; - } - } - - // Sort Contact processors based on Contact Link - uasort( $contact_processors, function( $a, $b ) { + // contact processors + $contacts = array_filter( $form['processors'], function( $processor ) { + return $processor['type'] === 'civicrm_contact'; + } ); + // sort contact processors by contact_link + uasort( $contacts, function( $a, $b ) { return $a['config']['contact_link'] - $b['config']['contact_link']; } ); - $form['processors'] = array_merge( $contact_processors, $rest_processors ); + $form['processors'] = array_merge( $contacts, array_diff_assoc( $form['processors'], $contacts ) ); return $form; } diff --git a/includes/class-civicrm-caldera-forms-helper.php b/includes/class-civicrm-caldera-forms-helper.php index 40fa436..289e6ca 100644 --- a/includes/class-civicrm-caldera-forms-helper.php +++ b/includes/class-civicrm-caldera-forms-helper.php @@ -50,6 +50,8 @@ class CiviCRM_Caldera_Forms_Helper { */ public $states; + public $current_contact_data; + /** * Initialises this object. * @@ -658,7 +660,6 @@ public function cached_price_sets() { * @return array $price_field_value The Price Field Value */ public function get_price_field_value( $id ) { - // when using a checkbox the value that gets passed is an array if ( is_array( $id ) ) $id = array_pop( $id ); @@ -667,13 +668,12 @@ public function get_price_field_value( $id ) { $price_field_value = civicrm_api3( 'PriceFieldValue', 'getsingle', [ 'return' => [ 'id', 'price_field_id', 'label', 'amount', 'count', 'membership_type_id', 'membership_num_terms', 'financial_type_id' ], 'id' => $id, - 'is_active' => 1, ] ); } catch ( CiviCRM_API3_Exception $e ) { } - if ( ! $price_field_value['is_error'] ) { + if ( $price_field_value ) { $price_field_value['amount'] = number_format( $price_field_value['amount'], 2, '.', '' ); return $price_field_value; } @@ -773,6 +773,8 @@ public function civi_contact_dedupe( $contact, $contact_type, $dedupe_rule_id ) */ public function current_contact_data_get() { + if ( ! empty( $this->current_contact_data ) ) return $this->current_contact_data; + $contact = false; // checksum links first @@ -790,8 +792,10 @@ public function current_contact_data_get() { } // logged in overrides checksum - if ( is_user_logged_in() ) + if ( is_user_logged_in() ) { $contact = $this->get_current_contact(); + $this->current_contact_data = $contact; + } return $contact; @@ -812,4 +816,93 @@ public function get_current_contact() { return false; } + /** + * Get a Participant custom fields. + * + * @since 1.0 + * @return array $custom_fields The array of custom fields - e.g. ['custom_x' => 'Label of custom_x'] + */ + public static function get_participant_custom_fields() { + + try { + $custom_groups = civicrm_api3( 'CustomGroup', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'extends' => 'Participant', + 'api.CustomField.get' => [ 'is_active' => 1, 'options' => [ 'limit' => 0 ] ], + 'options' => [ 'limit' => 0 ], + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + return [ 'note' => $e->getMessage(), 'type' => 'error' ]; + } + + $custom_fields = []; + foreach ( $custom_groups['values'] as $key => $custom_group ) { + foreach ( $custom_group['api.CustomField.get']['values'] as $k => $custom_field ) { + $custom_fields['custom_' . $custom_field['id']] = [ + 'label' => $custom_field['label'], + 'extends_entity_column_id' => $custom_group['extends_entity_column_id'], + 'extends_entity_column_value' => $custom_group['extends_entity_column_value'] + ]; + } + } + + return $custom_fields; + + } + + /** + * Get processor by type. + * + * @since 1.0 + * @param string $processor_type The processor type + * @param array $form Form config + * @return array $processors The processors config + */ + public function get_processor_by_type( $processor_type, $form ) { + // get form processors + $processors = Caldera_Forms::get_processor_by_type( $processor_type, $form ); + // filter out non associative keys + if ( $processors ) + return array_filter( $processors, function( $processor, $id ) { + return $id === $processor['ID']; + }, ARRAY_FILTER_USE_BOTH ); + + return false; + } + + /** + * Get processor id from magic tag. + * + * @since 1.0 + * @param string $magic_tag The processor_id magig tag + * @param array|boolean $form The form config or false + * @return string|boolean $processor_id The processor_id or false otherwise + */ + public function get_processor_from_magic( $magic_tag, $form = false ) { + + if ( ! is_string( $magic_tag ) ) return false; + + if ( strpos( $magic_tag, '{' ) === false ) return false; + + if ( strpos( $magic_tag, 'processor_id' ) === false ) return false; + + // clean up magic tag + $magic_tag = str_replace( [ '{', '}' ], '', $magic_tag ); + // get parts + $parts = explode( ':', $magic_tag ); + + if( ! $form ) global $form; + + // if form has more than one processor of same type + // the magic tag has the format of processor_type:processor_id: + // otherwise the format is processor_type:processor_id + if ( count( $parts ) > 2 ) { + return array_pop( $parts ); + } else { + return key( $this->get_processor_by_type( $parts[0], $form ) ); + } + + } + } diff --git a/includes/class-civicrm-caldera-forms-processors.php b/includes/class-civicrm-caldera-forms-processors.php index f41669b..ee14e3b 100644 --- a/includes/class-civicrm-caldera-forms-processors.php +++ b/includes/class-civicrm-caldera-forms-processors.php @@ -68,7 +68,6 @@ private function include_files() { // Include processor classes include CF_CIVICRM_INTEGRATION_PATH . 'processors/contact/class-contact-processor.php'; - // include CF_CIVICRM_INTEGRATION_PATH . 'processors/order/class-order-processor.php'; if ( in_array( 'CiviContribute', $this->enabled_components ) ) { include CF_CIVICRM_INTEGRATION_PATH . 'processors/order/class-order-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/line-item/class-line-item-processor.php'; @@ -76,6 +75,8 @@ private function include_files() { } if ( in_array( 'CiviMember', $this->enabled_components ) ) include CF_CIVICRM_INTEGRATION_PATH . 'processors/membership/class-membership-processor.php'; + if ( in_array( 'CiviEvent', $this->enabled_components ) ) + include CF_CIVICRM_INTEGRATION_PATH . 'processors/participant/class-participant-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/group/class-group-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/activity/class-activity-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/relationship/class-relationship-processor.php'; @@ -109,6 +110,8 @@ private function setup_objects() { } if ( in_array( 'CiviMember', $this->enabled_components ) ) $this->processors['membership'] = new CiviCRM_Caldera_Forms_Membership_Processor( $this->plugin ); + if ( in_array( 'CiviEvent', $this->enabled_components ) ) + $this->processors['participant'] = new CiviCRM_Caldera_Forms_Participant_Processor( $this->plugin ); if ( in_array( 'CiviCase', $this->enabled_components ) ) $this->processors['case'] = new CiviCRM_Caldera_Forms_Case_Processor( $this->plugin ); $this->processors['activity'] = new CiviCRM_Caldera_Forms_Activity_Processor( $this->plugin ); diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index fff62ac..8c9391c 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -81,13 +81,11 @@ public function register_processor( $processors ) { * @param array $form Form configuration */ public function pre_processor( $config, $form, $processid ) { - + } public function processor( $config, $form, $processid ) { - global $transdata; - $transient = $this->plugin->transient->get(); $this->contact_link = 'cid_' . $config['contact_link']; @@ -97,13 +95,14 @@ public function processor( $config, $form, $processid ) { $this->plugin->helper->get_price_field_value( $config['fixed_price_field_value'] ) : $this->plugin->helper->get_price_field_value( Caldera_Forms::do_magic_tags( $config['price_field_value'] ) ); - if ( ! empty( $config['entity_table'] ) ) { + if ( ! empty( $config['entity_table'] ) && $price_field_value ) { if ( $config['entity_table'] == 'civicrm_membership' ) { $price_field_value['entity_table'] = $config['entity_table']; $this->process_membership( $config, $form, $transient, $price_field_value ); } if ( $config['entity_table'] == 'civicrm_participant' ) { + $price_field_value['entity_table'] = $config['entity_table']; $this->process_participant( $config, $form, $transient, $price_field_value ); } @@ -111,7 +110,7 @@ public function processor( $config, $form, $processid ) { $price_field_value['entity_table'] = $config['entity_table']; $this->process_contribution( $config, $form, $transient, $price_field_value ); } - } else { + } elseif ( $price_field_value ) { $entity_table = $this->guess_entity_table( $price_field_value ); $price_field_value['entity_table'] = $entity_table; $entity = str_replace( 'civicrm_', '', $entity_table ); @@ -125,6 +124,7 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) // FIXME // only for memberships or contributions, need to find a way that checks for all tables + if ( ! isset( $this->price_sets ) ) $this->price_sets = $this->plugin->helper->cached_price_sets(); if ( ! empty( $entity_table ) ) return $entity_table; @@ -133,7 +133,6 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) // find entity table from priceset? - // $this->price_sets = $this->plugin->helper->cached_price_sets(); // foreach ( $this->price_sets as $price_set_id => $price_set ) { // foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { @@ -161,11 +160,7 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) */ public function process_membership( $config, $form, $transient, $price_field_value ) { - global $transdata; - $price_field_value['price_field_value_id'] = $price_field_value['id']; - - // $price_field_value['entity_table'] = $config['entity_table']; $price_field_value['field_title'] = $price_field_value['label']; $price_field_value['unit_price'] = $price_field_value['amount']; $price_field_value['qty'] = 1; @@ -214,6 +209,40 @@ public function process_membership( $config, $form, $transient, $price_field_val */ public function process_participant( $config, $form, $transient, $price_field_value ) { + // if price field is disabled by cfc we won't have a price_field_value + if ( ! $price_field_value['id'] ) return; + + $price_field_value['price_field_value_id'] = $price_field_value['id']; + $price_field_value['field_title'] = $price_field_value['label']; + $price_field_value['unit_price'] = $price_field_value['amount']; + $price_field_value['qty'] = 1; + $price_field_value['line_total'] = $price_field_value['amount'] * $price_field_value['qty']; + + // membership params aka 'params' + $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); + + if ( isset( $transient->participants->$processor_id->params ) && ! empty( $config['entity_params'] ) ) { + + $entity_params = $transient->participants->$processor_id->params; + + $entity_params['source'] = ! empty( $entity_params['source'] ) ? + $entity_params['source'] : + $form['name']; + + $entity_params['fee_level'] = $price_field_value['label']; + $entity_params['fee_amount'] = $price_field_value['amount']; + + } + + $line_item = [ + 'line_item' => [ $price_field_value ], + 'params' => $entity_params + ]; + + $transient->line_items->{$config['processor_id']}->params = $line_item; + + $this->plugin->transient->save( $transient->ID, $transient ); + } /** diff --git a/processors/membership/class-membership-processor.php b/processors/membership/class-membership-processor.php index d407ca4..33d0579 100644 --- a/processors/membership/class-membership-processor.php +++ b/processors/membership/class-membership-processor.php @@ -54,7 +54,7 @@ public function __construct( $plugin ) { // filter form before rendering add_filter( 'caldera_forms_render_get_form', [ $this, 'pre_render' ] ); // render membership notices - add_action( 'caldera_forms_render_start', [ $this, 'current_membership_notices' ] ); + add_action( 'cfc_notices_to_render', [ $this, 'render_notices' ] ); } @@ -256,25 +256,24 @@ public function get_num_terms( $form_values, $price_field_value = false ) { /** * Membership notices. * - * @since 0.4.4 - * - * @param array $form Form config + * @since 1.0 + * @param array $notices The array of notices to render + * @return array $notices The filtered notices */ - public function current_membership_notices( $form ) { + public function render_notices( $notices ) { // output if ( isset( $this->has_memberships ) ) { - $class = "cfc-notices-{$form['ID']}"; - $out = "
"; foreach ( $this->has_memberships as $key => $membership ) { // FIXME // use CiviCRM's date setting $end_date = date_format( date_create( $membership['end_date'] ), 'F d, Y' ); - $out .= '
'; - $out .= sprintf( __( 'Your %1$s membership expires on %2$s.', 'caldera-forms-civicrm' ), $membership['membership_name'], $end_date ); - $out .= '
'; + $notices[] = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Your %1$s membership expires on %2$s.', 'caldera-forms-civicrm' ), $membership['membership_name'], $end_date ) + ]; } - $out .= '
'; - echo $out; } + + return $notices; } } diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index bfdc979..f91a593 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -171,16 +171,16 @@ public function processor( $config, $form, $processid ) { foreach ( $config_line_items as $item => $processor ) { if( ! empty( $processor ) ) { $processor = Caldera_Forms::do_magic_tags( $processor ); - if ( ! strpos( $processor, 'civicrm_line_item' ) ) { + // line item is enabled and is not empty + if ( ! strpos( $processor, 'civicrm_line_item' ) && ! empty( ( array ) $transient->line_items->$processor ) ) { $line_items[$count] = $transient->line_items->$processor->params; - if ( - isset( $line_items[$count]['params']['membership_type_id'] ) && $this->is_pay_later ) { + if ( isset( $line_items[$count]['params']['membership_type_id'] ) && $this->is_pay_later ) { // set membership as pending $line_items[$count]['params']['status_id'] = 'Pending'; $line_items[$count]['params']['is_override'] = 1; } + $count++; } - $count++; } else { unset( $config_line_items[$item] ); } @@ -243,7 +243,7 @@ public function post_processor( $config, $form, $processid ) { * @param string $template_path The template path * @param array $form Form config */ - $template_path = apply_filters( 'cfc_order_thank_you_template_path', CF_CIVICRM_INTEGRATION_PATH . 'template/thank-you.php', $_form ); + $template_path = apply_filters( 'cfc_order_thank_you_template_path', CF_CIVICRM_INTEGRATION_PATH . 'templates/thank-you.php', $_form ); $form_values = Caldera_Forms::get_submission_data( $_form ); diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index e69de29..13d931f 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -0,0 +1,618 @@ +plugin = $plugin; + // register this processor + add_filter( 'caldera_forms_get_form_processors', array( $this, 'register_processor' ) ); + + // build price field references, at both render and submission start + add_filter( 'caldera_forms_render_get_form', [ $this, 'get_set_necessary_data' ] ); + add_filter( 'caldera_forms_submit_get_form', [ $this, 'get_set_necessary_data' ], 20 ); + + // filter fields for notices and discount price fields + add_filter( 'caldera_forms_render_get_field', [ $this, 'filter_fields_config' ], 10, 2 ); + add_filter( 'caldera_forms_render_setup_field', [ $this, 'filter_fields_config' ], 10, 2 ); + add_filter( 'caldera_forms_render_field_structure', [ $this, 'filter_fields_config' ], 10, 2 ); + + // filter form when it renders + add_filter( 'caldera_forms_render_get_form', [ $this, 'pre_render' ] ); + + } + + /** + * Adds this processor to Caldera Forms. + * + * @since 1.0 + * @uses 'caldera_forms_get_form_processors' filter + * @param array $processors The existing processors + * @return array $processors The modified processors + */ + public function register_processor( $processors ) { + + $processors[$this->key_name] = [ + 'name' => __( 'CiviCRM Participant', 'caldera-forms-civicrm' ), + 'description' => __( 'Add CiviCRM Participant to event (for Event registration).', 'caldera-forms-civicrm' ), + 'author' => 'Andrei Mondoc', + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'processors/participant/config.php', + 'pre_processor' => [ $this, 'pre_processor' ], + 'processor' => [ $this, 'processor' ], + 'magic_tags' => [ 'processor_id' ] + ]; + + return $processors; + + } + + /** + * Form pre processor callback. + * + * @since 1.0 + * @param array $config Processor configuration + * @param array $form Form configuration + * @param string $processid The process id + */ + public function pre_processor( $config, $form, $processid ) { + + // cfc transient object + $transient = $this->plugin->transient->get(); + $this->contact_link = 'cid_' . $config['contact_link']; + + // Get form values + $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + + + if ( ! empty( $transient->contacts->{$this->contact_link} ) ) { + // event + $event = $this->events[$config['id']]; + + $form_values['contact_id'] = $transient->contacts->{$this->contact_link}; + $form_values['event_id'] = $config['id']; + $form_values['role_id'] = ( $config['role_id'] == 'default_role_id' ) ? $event['default_role_id'] : $config['role_id']; + $form_values['status_id'] = ( $config['status_id'] == 'default_status_id' ) ? 'Registered' : $config['status_id']; // default is registered + + // if multiple participant processors, we need to update $this->registrations + $this->is_registered_for( $form ); + + // check if should register participant + $notice = $this->get_notice( $config['processor_id'], $form ); + if ( $notice ) { + $notice['type'] = 'error'; + return $notice; + } + + // store data in transient + $transient->participants->{$config['processor_id']}->params = $form_values; + $this->plugin->transient->save( $transient->ID, $transient ); + + if ( ! $config['is_monetary'] ) { + try { + $create_participant = civicrm_api3( 'Participant', 'create', $form_values ); + } catch ( CiviCRM_API3_Exception $e ) { + $error = $e->getMessage() . '

' . $e->getTraceAsString() . '
'; + return [ 'note' => $error, 'type' => 'error' ]; + } + } + } + + } + + /** + * Form processor callback. + * + * @since 1.0 + * @param array $config Processor configuration + * @param array $form Form configuration + * @param string $processid The process id + */ + public function processor( $config, $form, $porcessid ) { + return [ 'processor_id' => $config['processor_id'] ]; + } + + /** + * Autopopulates Form with Civi data. + * + * @uses 'caldera_forms_render_get_form' filter + * @since 1.0 + * @param array $form The form + * @return array $form The modified form + */ + public function pre_render( $form ) { + + // render notices for non paid events + $this->render_notices_for_non_paid_events( $form ); + + return $form; + } + + public function get_set_necessary_data( $form ) { + + // get events data + $this->get_set_events( $form ); + // build price field references + $this->build_price_field_refs( $form ); + // get event registrations + $this->is_registered_for( $form ); + + return $form; + } + + /** + * Build Price Field fields references from Line Item processors for paid events. + * + * Stores a referene to the participant processor and the price field + * set in it's corresponding line item processor. + * + * @since 1.0 + * @param array $form The form config + * @return array $form The form config + */ + public function build_price_field_refs( $form ) { + + if ( ! empty( $this->price_field_refs ) ) return $this->price_field_refs; + + // participant processors + $participants = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $participants ) return; + + // line item processors + $line_items = $this->plugin->helper->get_processor_by_type( 'civicrm_line_item', $form ); + + if ( ! $line_items ) return; + + $this->price_field_refs = array_reduce( $line_items, function( $refs, $line_item ) use ( $form ) { + + if ( $line_item['config']['entity_table'] == $this->key_name ) { + // price_field field config + $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( str_replace( '%', '', $line_item['config']['price_field_value'] ), $form ); + // participant processor id + $participant_pid = $this->plugin->helper->get_processor_from_magic( $line_item['config']['entity_params'], $form ); + + $refs[$participant_pid] = $price_field_field['ID']; + + return $refs; + } + + }, [] ); + + } + + /** + * Get and set events. + * + * @since 1.0 + * @param int $id Event id + * @return array|boolean $event The event settings, or false + */ + public function get_set_events( $form ) { + + if ( ! empty( $this->events ) ) return $this->events; + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $processors ) return; + + // event ids set in form's participant processors + $this->event_ids = array_reduce( $processors, function( $event_ids, $processor ) { + $event_ids[$processor['ID']] = $processor['config']['id']; + return $event_ids; + }, [] ); + + // get events + try { + $events = civicrm_api3( 'Event', 'get', [ + 'sequential' => 1, + 'id' => [ 'IN' => array_values( $this->event_ids ) ] + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( $events['count'] ) { + // set events references + $this->events = array_reduce( $events['values'], function( $events, $event ) { + $event['participant_count'] = CRM_Event_BAO_Event::getParticipantCount( $event['id'] ); + $events[array_search( $event['id'], $this->event_ids )] = $event; + return $events; + }, [] ); + } + + } + + /** + * Alter discounted price sets/price fields options and append notices for paid events. + * + * If it's a paid event, we know what field in the form + * is setup as a price set/price field accessing $price_field_refs. + * + * @uses 'caldera_forms_render_get_field' filter + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @return array $field The field config + */ + public function filter_fields_config( $field, $form ) { + + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + // continue if no participants are present + if ( ! $processors ) return $field; + // field or field_structure + $field_ID = $field['id'] ? $field['id'] : $field['ID']; + // only if the current field is mapped to a price_set/price_field + if ( array_search( $field_ID, $this->price_field_refs ) ) { + + array_map( function( $processor_id, $field_id ) use ( &$field, $form, $processors ) { + + // only paid events will have a price set/price field + if ( ! $processors[$processor_id]['config']['is_monetary'] ) return; + + // append notice only at render structure stage + if ( current_filter() == 'caldera_forms_render_field_structure' && $field_id == $field['id'] ) { + + $notice = $this->get_notice( $processor_id, $form ); + + if ( ! $notice ) return; + + // notice html + $template_path = CF_CIVICRM_INTEGRATION_PATH . 'templates/notice.php'; + $html = $this->plugin->html->generate( $notice, $template_path ); + + if ( $notice['disabled'] ) { + $field['field_before'] = '
' . $field['field_before']; + $field['field_after'] = $field['field_after'] . '
'; + } + + $field['label_after'] = $field['label_after'] . $html; + + } + + // check for discounted price field at field setup and config stages + if ( ( current_filter() == 'caldera_forms_render_get_field' || current_filter() == 'caldera_forms_render_setup_field' ) && $field_id == $field['ID'] ) { + + // filter required config at setup stage as well + if ( $this->get_notice( $processor_id, $form )['disabled'] ) $field['required'] = 0; + + // get discount entity id + $discount_entity_id = CRM_Core_BAO_Discount::findSet( $processors[$processor_id]['config']['id'], 'civicrm_event' ); + + // alter price field options if we have a discount + if ( $discount_entity_id ) { + // get price set id for discounted price set + $price_set_id = CRM_Core_DAO::getFieldValue( 'CRM_Core_BAO_Discount', $discount_entity_id, 'price_set_id' ); + $price_set = $this->plugin->helper->cached_price_sets()[$price_set_id]; + // there's only one price field per discounted price set + $price_field = reset( $price_set['price_fields'] ); + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { + $options[$price_field_value['id']] = [ + 'value' => $price_field_value['id'], + 'label' => $price_field_value['label'] . ' - ' . $field['config']['price_field_currency'] . ' ' . $price_field_value['amount'], + 'calc_value' => $price_field_value['amount'] + ]; + return $options; + }, [] ); + + } + } + + }, array_keys( $this->price_field_refs ), $this->price_field_refs ); + } + + return $field; + } + + /** + * Get notice for participant processor. + * + * @since 1.0 + * @param string $processor_id The processor id + * @param array $form The form config + * @param boolean $add_filter Wheather to add 'cfc_notices_to_render' filter + * @return array $notice Notice data array + */ + public function get_notice( $processor_id, $form, $add_filter = false ) { + + $processor = $form['processors'][$processor_id]; + $event = $this->events[$processor_id]; + $participant = $this->registrations[$processor_id]; + + // notices filter + $filter = 'cfc_notices_to_render'; + // cfc_notices_to_render filter callback + $callback = function( $notices ) use ( $event, &$notice ) { + $notices[] = $notice; + return $notices; + }; + + // is registered + if ( $participant && $participant['event_id'] == $event['id'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Oops. It looks like you are already registered for the event %1$s. If you want to change your registration, or you think that this is an error, please contact the site administrator.', 'caldera-forms-civicrm' ), $event['title'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // registration start date + if ( isset( $event['registration_start_date'] ) && date( 'Y-m-d H:i:s' ) <= $event['registration_start_date'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Registration for the event %s is not yet opened.', 'caldera-forms-civicrm' ), $event['title'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // registration end date + if ( isset( $event['registration_end_date'] ) && date( 'Y-m-d H:i:s' ) >= $event['registration_end_date'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Registration for the event %1$s was closed on %2$s.', 'caldera-forms-civicrm' ), $event['title'], date_format( date_create( $event['registration_end_date'] ), 'F d, Y H:i' ) ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // is participant approval + if ( $event['requires_approval'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['approval_req_text'] ), + 'disabled' => false + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // has waitlist and is full + if ( $this->is_full( $event ) && $event['has_waitlist'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['waitlist_text'] ), + 'disabled' => false + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // event full + if ( $this->is_full( $event ) && ! $event['has_waitlist'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['event_full_text'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + } + + public function render_notices_for_non_paid_events( $form ) { + + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $processors ) return; + + array_map( function( $processor_id, $processor ) use( $form ) { + + // only for non paid events + if ( $processor['config']['is_monetary'] ) return; + + $this->get_notice( $processor_id, $form, $add_filter = true ); + + }, array_keys( $processors ), $processors ); + + } + + /** + * Get event. + * + * @since 1.0 + * @param int $id Event id + * @return array|boolean $event The event settings, or false + */ + public function get_event( $id ) { + + try { + $event = civicrm_api3( 'Event', 'getsingle', [ 'id' => $id ] ); + } catch( CiviCRM_API3_Exception $e ) { + $error = $e->getMessage() . '

' . $e->getTraceAsString() . '
'; + return [ 'note' => $error, 'type' => 'error' ]; + } + + if ( is_array( $event ) && ! $event['is_error'] ) { + // get count + $event['participant_count'] = CRM_Event_BAO_Event::getParticipantCount( $id ); + return $event; + } + + return false; + } + + /** + * Event is full. + * + * @since 1.0 + * @param array $event The event config + * @return boolean True if full, false otherwise + */ + public function is_full( $event ) { + if ( isset( $event['participant_count'] ) && isset( $event['max_participants'] ) ) + return $event['participant_count'] >= $event['max_participants']; + } + + /** + * Get default status. + * + * @since 1.0 + * @param array $event The event config + * @param array $config Processor config + * @return string $status The participant status + */ + public function default_status( $event, $config ) { + + if ( $config['status_id'] != 'default_status_id' ) return $config['status_id']; + + if ( $event['requires_approval'] ) return 'Awaiting approval'; + + if ( $event['has_waitlist'] && $this->is_full( $event ) ) return 'On waitlist'; + + return 'Registered'; + } + + /** + * Get contact event registrations. + * + * @since 1.0 + * @param array $form The form config + */ + public function is_registered_for( $form ) { + + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $processors ) return; + + // contact processors + $contacts = $this->plugin->helper->get_processor_by_type( 'civicrm_contact', $form ); + // return registrations if is set and only one contact, otherwise recalculate + if ( count( $contacts ) == 1 && ! empty( $this->registrations ) ) return $this->registrations; + + // cfc transient + $transient = $this->plugin->transient->get(); + + array_map( function( $processor ) use ( $transient ) { + + if ( ! isset( $processor['runtimes'] ) ) return; + + $contact_link = 'cid_' . $processor['config']['contact_link']; + + if ( ! isset( $transient->contacts->{$contact_link} ) || empty( $transient->contacts->{$contact_link} ) ) return; + + $params = [ + 'contact_id' => $transient->contacts->{$contact_link}, + 'event_id' => [ 'IN' => array_values( $this->event_ids ) ] + ]; + + try { + $participant = civicrm_api3( 'Participant', 'get', $params ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( $participant['count'] ) { + array_map( function( $participant ) use ( $processor ) { + + $event_id = $this->event_ids[$processor['ID']]; + + if ( $participant['event_id'] == $event_id ) + $this->registrations[$processor['ID']] = $participant; + + }, $participant['values'] ); + } + + }, $processors ); + } + +} diff --git a/processors/participant/config.php b/processors/participant/config.php new file mode 100644 index 0000000..66b6c20 --- /dev/null +++ b/processors/participant/config.php @@ -0,0 +1,163 @@ + 1, + 'action' => 'create', +] ); + +$participant_roles = civicrm_api3( 'Participant', 'getoptions', [ + 'sequential' => 1, + 'field' => 'participant_role_id', +] ); + +$participant_statuses = civicrm_api3( 'Participant', 'getoptions', [ + 'sequential' => 1, + 'field' => 'participant_status_id', +] ); + +$participant_fields = []; +foreach ( $fields['values'] as $key => $value ) { + $participant_fields[$value['name']] = $value['title']; +} + +$ignore = [ 'event_id', 'contact_id', 'is_test', 'discount_amount', 'cart_id', 'must_wait', 'transferred_to_contact_id', 'id', 'status_id', 'role_id', 'register_date', 'fee_level', 'is_pay_later', 'fee_amount', 'register_by_id', 'discount_id', 'fee_currency', 'campaign_id' ]; + +$current_fields = [ 'source' ]; + +$events = civicrm_api3( 'Event', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'is_online_registration' => 1, + 'is_template' => 0, + 'options' => [ 'limit' => 0 ], +] ); + +$campaigns = civicrm_api3( 'Campaign', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'options' => [ 'limit' => 0 ], +] ); + +?> + +
+
+ +
+
+ +

+ + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ + +

+ $value ) { + if( in_array( $key, $current_fields ) ) { + ?> +
+ +
+ +
+
+ + +

+helper->get_participant_custom_fields() as $key => $custom_field ) { ?> +
+ +
+ +
+
+ + + diff --git a/templates/notice.php b/templates/notice.php new file mode 100644 index 0000000..808515a --- /dev/null +++ b/templates/notice.php @@ -0,0 +1,5 @@ + +
+ +
+ \ No newline at end of file diff --git a/templates/notices.php b/templates/notices.php new file mode 100644 index 0000000..a03c3b4 --- /dev/null +++ b/templates/notices.php @@ -0,0 +1,25 @@ + +
+ + +
+
+ +
+
+ + +
\ No newline at end of file diff --git a/template/thank-you.php b/templates/thank-you.php similarity index 100% rename from template/thank-you.php rename to templates/thank-you.php From 39236b656ce7c25adde642862fe8870f82a34491 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 19 Nov 2018 19:06:35 +0000 Subject: [PATCH 03/68] price_sets improvements and support for tax amounts --- .../class-civicrm-price-sets-presets.php | 146 ++++++++++++-- .../class-civicrm-caldera-forms-helper.php | 182 +++++++++++++++++- processors/order/class-order-processor.php | 8 + 3 files changed, 310 insertions(+), 26 deletions(-) diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index e37b9b8..799c25e 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -49,6 +49,9 @@ public function register_hooks() { // auto-populate Price Fields add_action( 'caldera_forms_autopopulate_types', [ $this, 'autopopulate_price_field_types' ] ); add_filter( 'caldera_forms_render_get_field', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); + add_filter( 'caldera_forms_render_setup_field', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); + + add_filter( 'caldera_forms_render_field_structure', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); } @@ -113,26 +116,135 @@ public function autopopulate_price_field_types() { */ public function autopopulate_price_field_values( $field, $form ) { - if ( $this->price_sets ) { - if ( ! empty( $field['config']['auto'] ) ) { - foreach ( $this->price_sets as $price_set_id => $price_set ) { - foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { - if( $field['config']['auto_type'] == 'cfc_price_field_' . $price_field_id ) { - foreach ( $price_field['price_field_values'] as $value_id => $price_field_value ) { - $field['config']['option'][$value_id] = [ - 'value' => $value_id, - 'label' => $price_field_value['label'] . ' - ' . $field['config']['price_field_currency'] . ' ' . $price_field_value['amount'], - 'calc_value' => $price_field_value['amount'] - ]; - } - } - } - } + // filter field structure + if ( current_filter() == 'caldera_forms_render_field_structure' ) + $field = $this->filter_price_field_structure( $field, $form ); + + if ( ! $this->is_price_field_field( $field, $form ) ) return $field; + + /** + * if we reach here, current $field is a 'price_field' field + */ + $price_field = $this->get_price_field_from_config( $field ); + + // remove field if not active + if ( ! $this->is_price_field_active( $price_field ) ) + return false; + + // populate field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { + + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $price_field_value['label'], $this->plugin->helper->format_money( $price_field_value['amount'] ) ), + 'calc_value' => $price_field_value['amount'] + ]; + + if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $price_field_value['label'], $price_field_value['amount'], $price_field_value['tax_amount'] ); } - } + + $options[$price_field_value['id']] = $option; + return $options; + + }, [] ); + + /** + * Filter autopopulated price fields. + * + * Triggers for each autopopualted price field at both config and setup stages, + * uses both 'caldera_forms_render_get_field' and 'caldera_forms_render_setup_field' filters. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @param array $price_field The price field and it's price_field_values + * @param string $current_filter The current filter + */ + $field = apply_filters( 'cfc_filter_price_field_config', $field, $form, $price_field, $current_filter = current_filter() ); return $field; } -} + /** + * Filter autopopulated price field's field structure. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @return array $field The filtered field + */ + public function filter_price_field_structure( $field, $form ) { + + if ( empty( $field['field']['config']['auto'] ) ) return $field; + + if ( strpos( $field['field']['config']['auto_type'], 'cfc_price_field_' === false ) ) return $field; + + /** + * if we reach here, current $field is a 'price_field' field + */ + $price_field = $this->get_price_field_from_config( $field['field'] ); + + /** + * Chance to alter autopopulated price fields. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @param array $price_field The price field and it's price_field_values + */ + $field = apply_filters( 'cfc_filter_price_field_structure', $field, $form, $price_field ); + + return $field; + } + /** + * Check if price field is active based on active_on/expire_on. + * + * @since 1.0 + * @param array $price_field The price field + * @return boolean $is_active Whether is active or not + */ + public function is_price_field_active( $price_field ) { + + $now = date( 'Y-m-d H:m:i' ); + $active_on = array_key_exists( 'active_on', $price_field ) ? $price_field['active_on'] : $now; + $expire_on = array_key_exists( 'expire_on', $price_field ) ? $price_field['expire_on'] : $now; + + return ( $now >= $active_on ) && ( $now <= $expire_on ); + + } + + /** + * Get price field config from field config. + * + * @since 1.0 + * @param array $field Field config + * @return array $price_field Price field config + */ + public function get_price_field_from_config( $field ) { + $price_field_id = ( int ) str_replace( 'cfc_price_field_', '', $field['config']['auto_type'] ); + return $this->plugin->helper->get_price_set_column_by_id( $price_field_id, 'price_field' ); + } + + /** + * Check if field is a price field. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form config + * @return boolean $is_price_field_field Whether the field is a price field or not + */ + public function is_price_field_field( $field, $form ) { + + if ( empty( $field['config']['auto'] ) ) return false; + + if ( strpos( $field['config']['auto_type'], 'cfc_price_field_' === false ) ) return false; + + if ( ! $this->price_sets ) return false; + + return true; + } + +} diff --git a/includes/class-civicrm-caldera-forms-helper.php b/includes/class-civicrm-caldera-forms-helper.php index 289e6ca..ed65f88 100644 --- a/includes/class-civicrm-caldera-forms-helper.php +++ b/includes/class-civicrm-caldera-forms-helper.php @@ -52,6 +52,24 @@ class CiviCRM_Caldera_Forms_Helper { public $current_contact_data; + /** + * CiviCRM tax and invoicing settings. + * + * @since 1.0 + * @access public + * @var array $tax_settings + */ + public $tax_settings; + + /** + * CiviCRM tax rates. + * + * @since 1.0 + * @access public + * @var array $tax_rates Holds tax rates in the form of [ => ] + */ + public $tax_rates; + /** * Initialises this object. * @@ -474,6 +492,8 @@ public function get_enabled_extensions(){ try { $result = civicrm_api3( 'Extension', 'get', [ 'sequential' => 1, + 'status' => 'installed', + 'statusLabel' => 'Enabled', 'options' => [ 'limit' => 0 ], ] ); } catch ( CiviCRM_API3_Exception $e ) { @@ -566,6 +586,106 @@ public function get_field_data_by_slug( $slug, $form ) { return Caldera_Forms::get_field_data( $field['ID'], $form ); } + /** + * Get CiviCRM tax and invoicing settings. + * + * @since 1.0 + * @return array $tax_settings + */ + public function get_tax_settings() { + if ( is_array( $this->tax_settings ) ) return $this->tax_settings; + $this->tax_settings = $this->get_civicrm_settings( 'contribution_invoice_settings' ); + return $this->tax_settings; + } + + /** + * Get CiviCRM tax rates. + * + * @since 1.0 + * @return array|bool Array of tax rates in the form of [ => ] + */ + public function get_tax_rates() { + + if ( is_array( $this->tax_rates ) ) return $this->tax_rates; + + $tax_financial_accounts = civicrm_api3( 'EntityFinancialAccount', 'get', [ + 'return' => [ + 'id', + 'entity_table', + 'entity_id', + 'account_relationship', + 'financial_account_id', + 'financial_account_id.financial_account_type_id', + 'financial_account_id.tax_rate' + ], + 'financial_account_id.is_tax' => 1, + 'options' => [ 'limit' => 0 ] + ] ); + + if ( $tax_financial_accounts['count'] ) { + // buils tax rates + $this->tax_rates = array_reduce( $tax_financial_accounts['values'], function( $tax_rates, $financial_account ) { + $tax_rates[$financial_account['entity_id']] = $financial_account['financial_account_id.tax_rate']; + return $tax_rates; + }, [] ); + + return $this->tax_rates; + + } + + return false; + } + + /** + * Calculate percentage for a given amount. + * + * @since 1.0 + * @param string $amount The amount + * @param string $percentage The percentage + * @return string $amount Calculated percentage amount + */ + public function calculate_percentage( $amount, $percentage ) { + return ( $percentage / 100 ) * $amount; + } + + /** + * Format tax label as per CiviCRM. + * + * @param string $label The label + * @param string $amount The amount + * @param string $tax_amount The tax amount + * @return string $label The formated label + */ + public function format_tax_label( $label, $amount, $tax_amount, $currency = false ) { + + $tax_settings = $this->get_tax_settings(); + $tax_term = $tax_settings['tax_term']; + $taxed_amount = $this->format_money( $amount + $tax_amount, $currency ); + $tax_amount = $this->format_money( $tax_amount, $currency ); + $amount = $this->format_money( $amount, $currency ); + + $format = [ + 'Do_not_show' => sprintf( '%1$s - %2$s', $label, $taxed_amount ), + 'Inclusive' => sprintf( '%1$s - %2$s (includes %3$s of %4$s)', $label, $taxed_amount, $tax_term, $tax_amount ), + 'Exclusive' => sprintf( '%1$s - %2$s + %3$s %4$s', $label, $amount, $tax_amount, $tax_term ) + ]; + + return $format[$tax_settings['tax_display_settings']]; + + } + + /** + * Format money, as per CiviCRM settings. + * + * @since 1.0 + * @param string $amount The amount + * @param string $currency Optional, the currency + * @return string $formated_amount The formated amount + */ + public function format_money( $amount, $currency = false ) { + return CRM_Utils_Money::format( $amount, $currency ); + } + /** * Get price sets. * @@ -573,6 +693,11 @@ public function get_field_data_by_slug( $slug, $form ) { * @return array $price_sets The active price sets with their corresponding price fields and price filed values */ public function get_price_sets() { + + // get tax settings + $tax_settings = $this->get_tax_settings(); + // get tax rates + $tax_rates = $this->get_tax_rates(); $price_set_params = [ 'sequential' => 1, @@ -587,6 +712,7 @@ public function get_price_sets() { ], ]; + try { $result_price_sets = civicrm_api3( 'PriceSet', 'get', $price_set_params ); } catch ( CiviCRM_API3_Exception $e ) { @@ -617,8 +743,14 @@ public function get_price_sets() { $price_set['price_set_id'] = $price_set_id = $price_set['id']; $price_set['price_fields'] = $price_set['api.PriceField.get']['values']; foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + $price_set['price_fields'][$price_field_id]['price_field_id'] = $price_field_id; foreach ( $price_field_values as $value_id => $price_field_value) { + $price_field_value['price_field_value_id'] = $value_id; if ( $price_field_id == $price_field_value['price_field_id'] ) { + if ( $tax_settings['invoicing'] && $tax_rates && array_key_exists( $price_field_value['financial_type_id'], $tax_rates ) ) { + $price_field_value['tax_rate'] = $tax_rates[$price_field_value['financial_type_id']]; + $price_field_value['tax_amount'] = $this->calculate_percentage( $price_field_value['amount'], $price_field_value['tax_rate'] ); + } $price_set['price_fields'][$price_field_id]['price_field_values'][$value_id] = $price_field_value; } } @@ -664,21 +796,53 @@ public function get_price_field_value( $id ) { if ( is_array( $id ) ) $id = array_pop( $id ); - try { - $price_field_value = civicrm_api3( 'PriceFieldValue', 'getsingle', [ - 'return' => [ 'id', 'price_field_id', 'label', 'amount', 'count', 'membership_type_id', 'membership_num_terms', 'financial_type_id' ], - 'id' => $id, - ] ); - } catch ( CiviCRM_API3_Exception $e ) { + $price_field_value = $this->get_price_set_column_by_id( $id, 'price_field_value' ); + // filter price field value + $price_field_value = apply_filters( 'cfc_filter_price_field_value_get', $price_field_value, $id ); + return $price_field_value; + } + + /** + * Get price_set/price_field/price_field_value by id specifing the column name. + * + * @since 1.0 + * @param int $id The entity id + * @param string $column_name The column name, price_set|price_field|price_field_value + * @return array $column The requested entity or false + */ + public function get_price_set_column_by_id( $id, $column_name ) { + + $price_sets = $this->cached_price_sets(); + + if ( $column_name == 'price_set' && array_key_exists( $id, $price_set ) ) { + $column = $price_set[$id]; } - if ( $price_field_value ) { - $price_field_value['amount'] = number_format( $price_field_value['amount'], 2, '.', '' ); - return $price_field_value; + if ( $column_name == 'price_field' ) { + foreach ( $price_sets as $price_set_id => $price_set ) { + foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + if ( array_key_exists( $id, $price_set['price_fields'] ) ) + $column = $price_set['price_fields'][$id]; + } + } } + if ( $column_name == 'price_field_value' ) { + foreach ( $price_sets as $price_set_id => $price_set ) { + foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + foreach ( $price_field['price_field_values'] as $price_field_value_id => $price_field_value ) { + if ( array_key_exists( $id, $price_field['price_field_values'] ) ) + $column = $price_field['price_field_values'][$id]; + } + } + } + } + + if ( $column ) return $column; + return false; + } /** diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index f91a593..a31c549 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -168,12 +168,16 @@ public function processor( $config, $form, $processid ) { // line items $line_items = []; $count = 0; + $total_tax_amount = 0; foreach ( $config_line_items as $item => $processor ) { if( ! empty( $processor ) ) { $processor = Caldera_Forms::do_magic_tags( $processor ); // line item is enabled and is not empty if ( ! strpos( $processor, 'civicrm_line_item' ) && ! empty( ( array ) $transient->line_items->$processor ) ) { $line_items[$count] = $transient->line_items->$processor->params; + // tax amount + if ( isset( $line_items[$count]['line_item'][0]['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) $total_tax_amount += $line_items[$count]['line_item'][0]['tax_amount']; + // membership is pay later if ( isset( $line_items[$count]['params']['membership_type_id'] ) && $this->is_pay_later ) { // set membership as pending $line_items[$count]['params']['status_id'] = 'Pending'; @@ -186,6 +190,10 @@ public function processor( $config, $form, $processid ) { } } + // add total tax amount + if ( $total_tax_amount ) + $form_values['tax_amount'] = $total_tax_amount; + $form_values['line_items'] = $line_items; // stripe metadata From 37140ba9ff9035001f4b9ee9b0b13efa8676b5b3 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 19 Nov 2018 19:08:03 +0000 Subject: [PATCH 04/68] participant processor improvements and integration with CiviDiscount extension --- caldera-forms-civicrm.php | 14 + fields/discount/class-civicrm-discount.php | 107 ++++ fields/discount/config.php | 0 fields/discount/field.php | 73 +++ fields/discount/js/cividiscount.js | 0 fields/discount/preview.php | 8 + includes/class-civicrm-caldera-forms-ajax.php | 24 + ...ass-civicrm-caldera-forms-cividiscount.php | 226 ++++++++ .../class-civicrm-caldera-forms-fields.php | 6 +- .../line-item/class-line-item-processor.php | 39 +- .../class-participant-processor.php | 512 ++++++++++++------ 11 files changed, 850 insertions(+), 159 deletions(-) create mode 100644 fields/discount/class-civicrm-discount.php create mode 100644 fields/discount/config.php create mode 100644 fields/discount/field.php create mode 100644 fields/discount/js/cividiscount.js create mode 100644 fields/discount/preview.php create mode 100644 includes/class-civicrm-caldera-forms-cividiscount.php diff --git a/caldera-forms-civicrm.php b/caldera-forms-civicrm.php index 572fc7b..18a2597 100644 --- a/caldera-forms-civicrm.php +++ b/caldera-forms-civicrm.php @@ -137,6 +137,15 @@ class CiviCRM_Caldera_Forms { */ public $html; + /** + * CiviDiscount helper object. + * + * @since 1.0 + * @access public + * @var object $cividiscount The CiviDiscount helper object + */ + public $cividiscount; + /** * Returns a single instance of this object when called. * @@ -228,6 +237,8 @@ private function include_files() { include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-transient.php'; // Include html class include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-html.php'; + // include CiviDiscount helper class + include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-cividiscount.php'; } @@ -260,6 +271,9 @@ private function setup_objects() { $this->assets = new CiviCRM_Caldera_Forms_Assets( $this ); // init html class $this->html = new CiviCRM_Caldera_Forms_HTML( $this ); + // init cividiscount class + if ( $this->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->processors->enabled_extensions ) ) + $this->cividiscount = new CiviCRM_Caldera_Forms_CiviDiscount( $this ); } diff --git a/fields/discount/class-civicrm-discount.php b/fields/discount/class-civicrm-discount.php new file mode 100644 index 0000000..7fdf18a --- /dev/null +++ b/fields/discount/class-civicrm-discount.php @@ -0,0 +1,107 @@ +plugin = $plugin; + // register Caldera Forms callbacks + $this->register_hooks(); + + } + + /** + * Register hooks. + * + * @since 1.0 + */ + public function register_hooks() { + + // add custom fields to Caldera UI + add_filter( 'caldera_forms_get_field_types', [ $this, 'register_field_type' ] ); + add_filter( 'caldera_forms_render_get_form', [ $this, 'localize_scripts' ], 10 ); + + } + + /** + * Adds the field definition for this field type to Caldera UI. + * + * @uses 'caldera_forms_get_field_types' filter + * + * @since 1.0 + * + * @param array $field_types The existing fields configuration + * @return array $field_types The modified fields configuration + */ + public function register_field_type( $field_types ) { + + $field_types[$this->key_name] = [ + 'field' => __( 'CiviCRM Discount', 'caldera-forms-civicrm' ), + 'file' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/field.php', + 'category' => __( 'CiviCRM', 'caldera-forms-civicrm' ), + 'description' => __( 'CiviCRM Discount field (CiviDiscount integration)', 'caldera-forms-civicrm' ), + 'setup' => [ + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/config.php', + 'preview' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/preview.php', + 'default' => [ + 'placeholder' => __( 'Insert code', 'caldera-forms-civicrm' ), + ], + ], + 'scripts' => [ + CF_CIVICRM_INTEGRATION_URL . 'fields/discount/js/cividiscount.js' + ] + ]; + + return $field_types; + + } + + /** + * Enqueue scripts + * + * @since 0.4.4 + * + * @param array $form Form config + * @return array $form Form config + */ + public function localize_scripts( $form ) { + $reference = false; + + foreach ( $form['fields'] as $field_id => $field ) { + if ( $field['type'] == $this->key_name ) { + $reference = true; + break; + } + } + + if( $reference ) { + wp_localize_script( 'cf-cividiscountjs', 'cfc', [ 'url' => admin_url( 'admin-ajax.php' ) ] ); + } + return $form; + } + +} diff --git a/fields/discount/config.php b/fields/discount/config.php new file mode 100644 index 0000000..e69de29 diff --git a/fields/discount/field.php b/fields/discount/field.php new file mode 100644 index 0000000..214dadd --- /dev/null +++ b/fields/discount/field.php @@ -0,0 +1,73 @@ + + + + + + + + + + \ No newline at end of file diff --git a/fields/discount/js/cividiscount.js b/fields/discount/js/cividiscount.js new file mode 100644 index 0000000..e69de29 diff --git a/fields/discount/preview.php b/fields/discount/preview.php new file mode 100644 index 0000000..c42eb11 --- /dev/null +++ b/fields/discount/preview.php @@ -0,0 +1,8 @@ +
+ {{#unless hide_label}}{{label}}{{#if required}} *{{/if}}{{/unless}} +
+ + + {{caption}} +
+
\ No newline at end of file diff --git a/includes/class-civicrm-caldera-forms-ajax.php b/includes/class-civicrm-caldera-forms-ajax.php index 7bfaf0c..03925f0 100644 --- a/includes/class-civicrm-caldera-forms-ajax.php +++ b/includes/class-civicrm-caldera-forms-ajax.php @@ -36,6 +36,9 @@ public function register_hooks() { add_action( 'wp_ajax_flush_price_set_cache', [ $this, 'flush_price_set_cache' ] ); add_action( 'wp_ajax_civicrm_contact_reference_get', [ $this, 'civicrm_contact_reference_get' ] ); add_action( 'wp_ajax_nopriv_civicrm_contact_reference_get', [ $this, 'civicrm_contact_reference_get' ] ); + // event code discount + add_action( 'wp_ajax_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); + add_action( 'wp_ajax_nopriv_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); } @@ -155,4 +158,25 @@ public function flush_price_set_cache() { } die; } + + public function do_code_cividiscount() { + + if ( ! wp_verify_nonce( $_POST['nonce'], 'civicrm_cividiscount_code' ) ) return; + if ( isset( $_POST['cividiscount_code'] ) ) $code = $_POST['cividiscount_code']; + if ( isset( $_POST['form_id'] ) ) $form_id = $_POST['form_id']; + if ( isset( $_POST['form_id_attr'] ) ) $form_id_attr = $_POST['form_id_attr']; + + $discount = $this->plugin->cividiscount->get_by_code( $code ); + + if ( $discount ) { + // form config + $form = Caldera_Forms::get_form( $form_id ); + // add count + $form['form_count'] = str_replace( $form['ID'].'_', '', $form_id_attr ); + $discounted_options = $this->plugin->cividiscount->do_code_event_discount_options( $discount, $form ); + } + + echo json_encode( $discounted_options ); + die; + } } diff --git a/includes/class-civicrm-caldera-forms-cividiscount.php b/includes/class-civicrm-caldera-forms-cividiscount.php new file mode 100644 index 0000000..1a0d2eb --- /dev/null +++ b/includes/class-civicrm-caldera-forms-cividiscount.php @@ -0,0 +1,226 @@ + => ] + */ + public $event_cividiscounts; + + /** + * CiviDiscount criteria/filters, + * whether the criteria is met for a processor_id. + * + * @since 1.0 + * @access public + * @var array $event_autodiscounts Reference to [ => true|false ] + */ + public $event_autodiscounts; + + /** + * Initialises this object. + * + * @since 1.0 + */ + public function __construct( $plugin ) { + $this->plugin = $plugin; + } + + /** + * Get CiviDiscounts for events. + * + * @since 1.0 + * @param array $event_ids The event ids to get discounts for + * @return array $event_cividiscounts Discounts per processors array + */ + public function get_event_cividiscounts( $event_ids ) { + + if ( is_array( $this->event_cividiscounts ) ) return $this->event_cividiscounts; + + $discount = civicrm_api3( 'DiscountCode', 'get', [ + 'events' => [ 'IN' => array_values( $event_ids ) ], + 'is_active' => 1 + ] ); + + $event_cividiscounts = array_reduce( $discount['values'], function( $discounts, $discount ) use ( $event_ids ) { + $processor_id = array_search( array_pop( $discount['events'] ), $event_ids ); + $discount['autodiscount'] = json_decode( $discount['autodiscount'], true ); + + if ( $processor_id ) + $discounts[$processor_id] = $discount; + + return $discounts; + + }, [] ); + + $this->event_cividiscounts = $event_cividiscounts; + + return $this->event_cividiscounts; + + } + + /** + * Check autodiscount for a contact. + * + * @since 1.0 + * @param array $autodiscount CiviDiscount autodiscount (criteria/filter) property + * @param int $contact_id The contact id + * @param string $processor_id The praticipant processor id + * @return bool $is_autodiscount + */ + public function check_autodiscount( $autodiscount, $contact_id, $processor_id ) { + + if ( isset( $this->event_autodiscounts[$processor_id] ) ) return $this->event_autodiscounts[$processor_id]; + + $is_autodiscount = false; + + if ( ! empty( $autodiscount ) ) { + foreach ( $autodiscount as $entity => $params ) { + $params['contact_id'] = $contact_id; + try { + $result = civicrm_api3( $entity, 'getsingle', $params ); + if ( $result['count'] || isset( $result['id'] ) ) { + $is_autodiscount = true; + break; + } + } catch ( CiviCRM_API3_Exception $e ) { + + } + } + } + $this->event_autodiscounts[$processor_id] = $is_autodiscount; + + return $is_autodiscount; + } + + public function get_by_code( $code ) { + + try { + $discount = civicrm_api3( 'DiscountCode', 'getsingle', [ + 'sequential' => 1, + 'code' => $code, + 'is_active' => 1 + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( $discount && ! $discount['is_error'] ) + return $discount; + + return false; + } + + public function do_discounted_option( $option, $field, $price_field_value, $event_discount ) { + + $label = sprintf( __( '%1$s (Includes automatic discount of: ', 'caldera-forms-civicrm' ), $price_field_value['label'] ); + + // percentage discount + if ( $event_discount['amount_type'] == 1 ) { + $discounted_amount = $price_field_value['amount'] - $this->plugin->helper->calculate_percentage( $price_field_value['amount'], $event_discount['amount'] ); + $label .= $event_discount['amount'] . __( '%)', 'caldera-forms-civicrm' ); + } + // fixed discount + if ( $event_discount['amount_type'] == 2 ) { + $discounted_amount = $price_field_value['amount'] - $event_discount['amount']; + $label .= $this->plugin->helper->format_money( $event_discount['amount'] ) . __( ')', 'caldera-forms-civicrm' ); + } + // filtered option + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $label, $this->plugin->helper->format_money( $discounted_amount ) ), + 'calc_value' => $discounted_amount, + 'disabled' => $option['disabled'] + ]; + + add_filter( 'cfc_filter_price_field_value_get', function( $field_value, $field_value_id ) use ( $price_field_value, $discounted_amount, $label ) { + if ( $field_value_id == $price_field_value['id'] ) { + $field_value['amount'] = $discounted_amount; + $field_value['label'] = $label; + } + + return $field_value; + + }, 10, 2 ); + + // has tax + if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $label, $discounted_amount, $price_field_value['tax_amount'] ); + } + + return $option; + } + + public function do_code_event_discount_options( $discount, $form ) { + + if ( ! $discount ) return; + + // participant processors + $participants = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + // price field references + $price_field_refs = $this->plugin->processors->processors['participant']->build_price_field_refs( $form ); + // field configs + $fields = array_reduce( $price_field_refs, function( $fields, $field_id ) use ( $form ) { + $fields[$field_id] = Caldera_Forms_Field_Util::get_field( $field_id, $form, $apply_filters = true ); + return $fields; + }, [] ); + + return array_reduce( $price_field_refs, function( $discounted_options, $field_id ) use ( $fields, $form, $participants, $discount, $price_field_refs ) { + + $processor_id = array_search( $field_id, $price_field_refs ); + + if ( ! in_array( $participants[$processor_id]['config']['id'], $discount['events'] ) ) return $discounted_options; + + $field = $fields[$field_id]; + + $price_field = $this->plugin->fields->presets_objects['civicrm_price_sets']->get_price_field_from_config( $field ); + + // $field = $this->plugin->processors->processors['participant']->do_event_autodiscounts( $field, $form, $processor_id, $price_field ); + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->do_discounted_option( $option, $field, $price_field_value, $discount ); + + return $options; + + }, [] ); + + $options = array_reduce( $field['config']['option'], function( $options, $option ) use ( $field_id, $field, $form ) { + $field_option_id = $field_id . '_' . $form['form_count'] . '_' . $option['value']; + $options[$field_option_id] = $field['config']['option'][$option['value']]; + $options[$field_option_id]['field_id'] = $field_id; + return $options; + }, [] ); + + return $discounted_options + $options; + + }, [] ); + } + + public function get_discount_fields( $form ) { + return array_filter( $form['fields'], function( $field ) { + return $field['type'] === 'civicrm_discount'; + } ); + } + +} diff --git a/includes/class-civicrm-caldera-forms-fields.php b/includes/class-civicrm-caldera-forms-fields.php index 0095ed6..3f0d93f 100644 --- a/includes/class-civicrm-caldera-forms-fields.php +++ b/includes/class-civicrm-caldera-forms-fields.php @@ -62,7 +62,7 @@ private function include_files() { include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-custom-fields-presets.php'; if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-price-sets-presets.php'; - + include CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/class-civicrm-discount.php'; } /** @@ -84,5 +84,9 @@ private function setup_objects() { if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) $this->presets_objects['civicrm_price_sets'] = new CiviCRM_Caldera_Forms_Price_Sets_Presets( $this->plugin ); + // discount field for cividiscount integration + if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) + $this->field_objects['civicrm_discount'] = new CiviCRM_Caldera_Forms_Field_Discount( $this->plugin ); + } } diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index 8c9391c..2b48753 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -95,6 +95,18 @@ public function processor( $config, $form, $processid ) { $this->plugin->helper->get_price_field_value( $config['fixed_price_field_value'] ) : $this->plugin->helper->get_price_field_value( Caldera_Forms::do_magic_tags( $config['price_field_value'] ) ); + unset( + $price_field_value['name'], + $price_field_value['weight'], + $price_field_value['is_default'], + $price_field_value['is_active'], + $price_field_value['visibility_id'] + ); + + // add tax amount + if ( isset( $price_field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) + $price_field_value['amount'] += $price_field_value['tax_amount']; + if ( ! empty( $config['entity_table'] ) && $price_field_value ) { if ( $config['entity_table'] == 'civicrm_membership' ) { $price_field_value['entity_table'] = $config['entity_table']; @@ -187,7 +199,7 @@ public function process_membership( $config, $form, $transient, $price_field_val $entity_params['is_price_field_based'] ); $line_item = [ - 'line_item' => [ $price_field_value ], + 'line_item' => [ $price_field_value['price_field_value_id'] => $price_field_value ], 'params' => $entity_params ]; @@ -200,7 +212,7 @@ public function process_membership( $config, $form, $transient, $price_field_val /** * Process Participant Line Item. * - * @since 0.4.4 + * @since 1.0 * * @param array $config Processor config * @param array $form Form config @@ -212,13 +224,17 @@ public function process_participant( $config, $form, $transient, $price_field_va // if price field is disabled by cfc we won't have a price_field_value if ( ! $price_field_value['id'] ) return; + // get price field + $price_field = $this->plugin->helper->get_price_set_column_by_id( $price_field_value['price_field_id'], 'price_field' ); + $price_field_value['price_field_value_id'] = $price_field_value['id']; - $price_field_value['field_title'] = $price_field_value['label']; + $price_field_value['label'] = $price_field_value['label']; + $price_field_value['field_title'] = $price_field['label']; $price_field_value['unit_price'] = $price_field_value['amount']; $price_field_value['qty'] = 1; $price_field_value['line_total'] = $price_field_value['amount'] * $price_field_value['qty']; - // membership params aka 'params' + // participant params aka 'params' $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); if ( isset( $transient->participants->$processor_id->params ) && ! empty( $config['entity_params'] ) ) { @@ -229,13 +245,25 @@ public function process_participant( $config, $form, $transient, $price_field_va $entity_params['source'] : $form['name']; + // need to set price set id, otherwise Participant.create from Order.create + // will create a non-linked LineItem as the contribution has been created yet + $entity_params['price_set_id'] = $price_field['price_set_id']; $entity_params['fee_level'] = $price_field_value['label']; $entity_params['fee_amount'] = $price_field_value['amount']; } + unset( + $price_field_value['id'], + $price_field_value['amount'], + $price_field_value['contribution_type_id'], + $price_field_value['non_deductible_amount'], + $entity_params['price_field_value'], + $entity_params['is_price_field_based'] + ); + $line_item = [ - 'line_item' => [ $price_field_value ], + 'line_item' => [ $price_field_value['price_field_value_id'] => $price_field_value ], 'params' => $entity_params ]; @@ -284,4 +312,5 @@ public function process_contribution( $config, $form, $transient, $price_field_v $this->plugin->transient->save( $transient->ID, $transient ); } + } diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index 13d931f..63d404d 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -42,6 +42,8 @@ class CiviCRM_Caldera_Forms_Participant_Processor { */ public $events; + public $event_cividiscounts; + /** * Current registration for a contact (participant data). * @@ -83,10 +85,9 @@ public function __construct( $plugin ) { add_filter( 'caldera_forms_render_get_form', [ $this, 'get_set_necessary_data' ] ); add_filter( 'caldera_forms_submit_get_form', [ $this, 'get_set_necessary_data' ], 20 ); - // filter fields for notices and discount price fields - add_filter( 'caldera_forms_render_get_field', [ $this, 'filter_fields_config' ], 10, 2 ); - add_filter( 'caldera_forms_render_setup_field', [ $this, 'filter_fields_config' ], 10, 2 ); - add_filter( 'caldera_forms_render_field_structure', [ $this, 'filter_fields_config' ], 10, 2 ); + // filter price fields for notices and discount price fields + add_filter( 'cfc_filter_price_field_config', [ $this, 'filter_price_field_config' ], 10, 4 ); + add_filter( 'cfc_filter_price_field_structure', [ $this, 'render_notices_for_paid_events' ], 10, 3 ); // filter form when it renders add_filter( 'caldera_forms_render_get_form', [ $this, 'pre_render' ] ); @@ -137,7 +138,7 @@ public function pre_processor( $config, $form, $processid ) { if ( ! empty( $transient->contacts->{$this->contact_link} ) ) { // event - $event = $this->events[$config['id']]; + $event = $this->events[$config['processor_id']]; $form_values['contact_id'] = $transient->contacts->{$this->contact_link}; $form_values['event_id'] = $config['id']; @@ -145,20 +146,17 @@ public function pre_processor( $config, $form, $processid ) { $form_values['status_id'] = ( $config['status_id'] == 'default_status_id' ) ? 'Registered' : $config['status_id']; // default is registered // if multiple participant processors, we need to update $this->registrations - $this->is_registered_for( $form ); + $this->registrations = $this->get_participant_registrations( $this->event_ids, $form ); + + $is_registered = is_array( $this->registrations[$config['processor_id']] ); - // check if should register participant - $notice = $this->get_notice( $config['processor_id'], $form ); - if ( $notice ) { - $notice['type'] = 'error'; - return $notice; + // store data in transient if is not registered + if ( ! $is_registered ) { + $transient->participants->{$config['processor_id']}->params = $form_values; + $this->plugin->transient->save( $transient->ID, $transient ); } - - // store data in transient - $transient->participants->{$config['processor_id']}->params = $form_values; - $this->plugin->transient->save( $transient->ID, $transient ); - if ( ! $config['is_monetary'] ) { + if ( ! $config['is_monetary'] && ! $is_registered ) { try { $create_participant = civicrm_api3( 'Participant', 'create', $form_values ); } catch ( CiviCRM_API3_Exception $e ) { @@ -198,14 +196,30 @@ public function pre_render( $form ) { return $form; } + /** + * Get and set necessary data. + * + * @since 1.0 + * @param array $form The form config + * @return array $form The form config + */ public function get_set_necessary_data( $form ) { + // participant processors + $participants = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + // bail early + if ( ! $participants ) return $form; + // get event ids + $this->event_ids = $this->get_events_ids( $participants ); // get events data - $this->get_set_events( $form ); + $this->events = $this->get_events_config( $this->event_ids ); // build price field references - $this->build_price_field_refs( $form ); + $this->price_field_refs = $this->build_price_field_refs( $form ); // get event registrations - $this->is_registered_for( $form ); + $this->registrations = $this->get_participant_registrations( $this->event_ids, $form ); + // get cividiscounts + if ( isset( $this->plugin->cividiscount ) ) + $this->event_cividiscounts = $this->plugin->cividiscount->get_event_cividiscounts( $this->event_ids ); return $form; } @@ -213,35 +227,25 @@ public function get_set_necessary_data( $form ) { /** * Build Price Field fields references from Line Item processors for paid events. * - * Stores a referene to the participant processor and the price field - * set in it's corresponding line item processor. - * * @since 1.0 * @param array $form The form config - * @return array $form The form config + * @return array|boolean $price_field_ref References to [ => ], or false */ public function build_price_field_refs( $form ) { - if ( ! empty( $this->price_field_refs ) ) return $this->price_field_refs; - - // participant processors - $participants = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); - - if ( ! $participants ) return; - // line item processors $line_items = $this->plugin->helper->get_processor_by_type( 'civicrm_line_item', $form ); - if ( ! $line_items ) return; + if ( ! $line_items ) return false; - $this->price_field_refs = array_reduce( $line_items, function( $refs, $line_item ) use ( $form ) { + return array_reduce( $line_items, function( $refs, $line_item ) use ( $form ) { - if ( $line_item['config']['entity_table'] == $this->key_name ) { + if ( $line_item['config']['entity_table'] == 'civicrm_participant' ) { // price_field field config $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( str_replace( '%', '', $line_item['config']['price_field_value'] ), $form ); // participant processor id $participant_pid = $this->plugin->helper->get_processor_from_magic( $line_item['config']['entity_params'], $form ); - + $refs[$participant_pid] = $price_field_field['ID']; return $refs; @@ -252,130 +256,358 @@ public function build_price_field_refs( $form ) { } /** - * Get and set events. + * Get events ids. * * @since 1.0 - * @param int $id Event id - * @return array|boolean $event The event settings, or false + * @param array $participant_processors Array holding participant processor config + * @return array|boolean $event_ids References to [ => ], or false */ - public function get_set_events( $form ) { + public function get_events_ids( $participant_processors ) { - if ( ! empty( $this->events ) ) return $this->events; - // participant processors - $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); - - if ( ! $processors ) return; + if ( ! $participant_processors ) return false; // event ids set in form's participant processors - $this->event_ids = array_reduce( $processors, function( $event_ids, $processor ) { + return array_reduce( $participant_processors, function( $event_ids, $processor ) { $event_ids[$processor['ID']] = $processor['config']['id']; return $event_ids; }, [] ); - // get events + } + + /** + * Get events settings. + * + * @since 1.0 + * @param array $event_ids Array of event ids to get the settings for + * @return array|boolean $events References to [ => (array) ], or false + */ + public function get_events_config( $event_ids ) { + + if ( ! $event_ids ) return false; + try { $events = civicrm_api3( 'Event', 'get', [ 'sequential' => 1, - 'id' => [ 'IN' => array_values( $this->event_ids ) ] + 'id' => [ 'IN' => array_values( $event_ids ) ] ] ); } catch ( CiviCRM_API3_Exception $e ) { } - if ( $events['count'] ) { - // set events references - $this->events = array_reduce( $events['values'], function( $events, $event ) { + if ( $events['count'] ) + return array_reduce( $events['values'], function( $events, $event ) use ( $event_ids ) { $event['participant_count'] = CRM_Event_BAO_Event::getParticipantCount( $event['id'] ); - $events[array_search( $event['id'], $this->event_ids )] = $event; + $events[array_search( $event['id'], $event_ids )] = $event; return $events; }, [] ); - } + return false; + } + + /** + * Check and filter discounted price fields options. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure + */ + public function filter_price_field_config( $field, $form, $price_field, $current_filter ) { + + if ( ! $this->price_field_refs ) return $field; + + if ( ! array_search( $field['ID'], $this->price_field_refs ) ) return $field; + + array_map( function( $processor_id, $field_id ) use ( &$field, $form, $price_field, $current_filter ) { + + if ( $field_id != $field['ID'] ) return; + + $notice = $this->get_notice( $processor_id, $form ); + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( &$field, $notice ) { + + $option = $field['config']['option'][$price_field_value['id']]; + // disable option and make sure field is not required + if ( $notice && $notice['disabled'] ) { + $option['disabled'] = true; + $field['required'] = 0; + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + + // check for discounted price field events + $field = $this->handle_discounted_events( $field, $form, $processor_id, $price_field ); + // do event cividiscounts + if ( isset( $this->plugin->cividiscount ) ) + $field = $this->do_event_autodiscounts( $field, $form, $processor_id, $price_field ); + if ( $current_filter != 'caldera_forms_render_field_structure' ) + $field = $this->do_event_code_discounts( $field, $form, $processor_id, $price_field ); + + $field = $this->handle_max_count_participants( $field, $form, $processor_id, $price_field ); + return $field; + + }, array_keys( $this->price_field_refs ), $this->price_field_refs ); + + return $field; + } + + /** + * Filter price field options for discounted events. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function handle_discounted_events( $field, $form, $processor_id, $price_field ) { + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + $event_id = $processor['config']['id']; + + + $discount_entity_id = CRM_Core_BAO_Discount::findSet( $event_id, 'civicrm_event' ); + + if ( ! $discount_entity_id ) return $field; + // get price set id for discounted price set + $price_set_id = CRM_Core_DAO::getFieldValue( 'CRM_Core_BAO_Discount', $discount_entity_id, 'price_set_id' ); + $price_set = $this->plugin->helper->cached_price_sets()[$price_set_id]; + // discounted price field + $price_field = array_pop( $price_set['price_fields'] ); + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { + + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $price_field_value['label'], $this->plugin->helper->format_money( $price_field_value['amount'] ) ), + 'calc_value' => $price_field_value['amount'] + ]; + + if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $price_field_value['label'], $price_field_value['amount'], $price_field_value['tax_amount'] ); + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + return $field; } /** - * Alter discounted price sets/price fields options and append notices for paid events. + * Filter price field options for discounted events. * - * If it's a paid event, we know what field in the form - * is setup as a price set/price field accessing $price_field_refs. + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function handle_max_count_participants( $field, $form, $processor_id, $price_field ) { + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + if ( ! count( array_column( $price_field['price_field_values'], 'max_value' ) ) ) return $field; + + $event_id = $processor['config']['id']; + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $event_id ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + $current_count = CRM_Event_BAO_Participant::priceSetOptionsCount( $event_id ); + + // disable option based on max value count + if ( array_key_exists( 'max_value', $price_field_value ) && $current_count[$price_field_value['id']] >= $price_field_value['max_value'] ) { + $option['disabled'] = true; + $option['label'] .= ' ' . __( '(Sold out!)', 'caldera-forms-civicrm' ); + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + return $field; + } + + /** + * Do event autidiscounts. * - * @uses 'caldera_forms_render_get_field' filter * @since 1.0 - * @param array $field The field config + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function do_event_autodiscounts( $field, $form, $processor_id, $price_field ) { + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + $event_discount = $this->event_cividiscounts[$processor_id]; + + if ( ! $event_discount ) return $field; + + $transient = $this->plugin->transient->get(); + + $contact_link = 'cid_' . $processor['config']['contact_link']; + $contact_id = property_exists( $transient->contacts, $contact_link ) && ! empty( $transient->contacts->$contact_link ) ? $transient->contacts->$contact_link : flase; + + // does the contact meet the autodiscount criteria? + if ( $contact_id ) + $is_autodiscount = $this->plugin->cividiscount->check_autodiscount( $event_discount['autodiscount'], $transient->contacts->$contact_link, $processor_id ); + // bail if not + if ( ! $is_autodiscount ) return $field; + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $event_discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $event_discount ); + + return $options; + }, [] ); + + return $field; + } + + /** + * Do code event discounts. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function do_event_code_discounts( $field, $form, $processor_id, $price_field ) { + + if ( ! isset( $this->plugin->cividiscount ) ) return $field; + + $discount_fields = $this->plugin->cividiscount->get_discount_fields( $form ); + + if ( ! $discount_fields ) return $field; + + array_map( function( $discount_field_id, $discount_field ) use ( &$field, $form, $processor_id, $price_field ) { + + $code = Caldera_Forms::get_field_data( $discount_field_id, $form ); + + if ( ! $code ) return; + + $discount = $this->plugin->cividiscount->get_by_code( $code ); + + if ( ! $discount ) return; + + if ( ! in_array( $this->event_ids[$processor_id], $discount['events'] ) ) return; + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( &$field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $discount ); + + return $options; + + }, [] ); + + return $field; + + }, array_keys( $discount_fields ), $discount_fields ); + + return $field; + + } + + /** + * Render notices for paid events. + * + * @since 1.0 + * @param array $field The field structure * @param array $form The form config - * @return array $field The field config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure */ - public function filter_fields_config( $field, $form ) { + public function render_notices_for_paid_events( $field, $form, $price_field ) { + + if ( ! $this->price_field_refs ) return $field; + + if ( ! array_search( $field['id'], $this->price_field_refs ) ) return $field; - // participant processors $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); - // continue if no participants are present - if ( ! $processors ) return $field; - // field or field_structure - $field_ID = $field['id'] ? $field['id'] : $field['ID']; - // only if the current field is mapped to a price_set/price_field - if ( array_search( $field_ID, $this->price_field_refs ) ) { + array_map( function( $processor_id, $field_id ) use ( &$field, $form, $processors ) { - array_map( function( $processor_id, $field_id ) use ( &$field, $form, $processors ) { + if ( $field_id != $field['id'] ) return; - // only paid events will have a price set/price field - if ( ! $processors[$processor_id]['config']['is_monetary'] ) return; + // only paid events will have a price set/price field + if ( ! $processors[$processor_id]['config']['is_monetary'] ) return; - // append notice only at render structure stage - if ( current_filter() == 'caldera_forms_render_field_structure' && $field_id == $field['id'] ) { + $notice = $this->get_notice( $processor_id, $form ); - $notice = $this->get_notice( $processor_id, $form ); + if ( ! $notice ) return; - if ( ! $notice ) return; - - // notice html - $template_path = CF_CIVICRM_INTEGRATION_PATH . 'templates/notice.php'; - $html = $this->plugin->html->generate( $notice, $template_path ); + // notice html + $template_path = CF_CIVICRM_INTEGRATION_PATH . 'templates/notice.php'; + $html = $this->plugin->html->generate( $notice, $template_path ); - if ( $notice['disabled'] ) { - $field['field_before'] = '
' . $field['field_before']; - $field['field_after'] = $field['field_after'] . '
'; - } + $field['label_after'] = $field['label_after'] . $html; - $field['label_after'] = $field['label_after'] . $html; + }, array_keys( $this->price_field_refs ), $this->price_field_refs ); - } + return $field; + } - // check for discounted price field at field setup and config stages - if ( ( current_filter() == 'caldera_forms_render_get_field' || current_filter() == 'caldera_forms_render_setup_field' ) && $field_id == $field['ID'] ) { - - // filter required config at setup stage as well - if ( $this->get_notice( $processor_id, $form )['disabled'] ) $field['required'] = 0; - - // get discount entity id - $discount_entity_id = CRM_Core_BAO_Discount::findSet( $processors[$processor_id]['config']['id'], 'civicrm_event' ); - - // alter price field options if we have a discount - if ( $discount_entity_id ) { - // get price set id for discounted price set - $price_set_id = CRM_Core_DAO::getFieldValue( 'CRM_Core_BAO_Discount', $discount_entity_id, 'price_set_id' ); - $price_set = $this->plugin->helper->cached_price_sets()[$price_set_id]; - // there's only one price field per discounted price set - $price_field = reset( $price_set['price_fields'] ); - - // filter field options - $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { - $options[$price_field_value['id']] = [ - 'value' => $price_field_value['id'], - 'label' => $price_field_value['label'] . ' - ' . $field['config']['price_field_currency'] . ' ' . $price_field_value['amount'], - 'calc_value' => $price_field_value['amount'] - ]; - return $options; - }, [] ); - - } - } + /** + * Render notices for paid events, at the top of the form. + * + * @since 1.o + * @param array $form The form config + * @return $form + */ + public function render_notices_for_non_paid_events( $form ) { - }, array_keys( $this->price_field_refs ), $this->price_field_refs ); - } + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $processors ) return; + + array_map( function( $processor_id, $processor ) use ( $form ) { + + // only for non paid events + if ( $processor['config']['is_monetary'] ) return; + // render notice + $this->get_notice( $processor_id, $form, $add_filter = true ); + + }, array_keys( $processors ), $processors ); - return $field; } /** @@ -487,24 +719,6 @@ public function get_notice( $processor_id, $form, $add_filter = false ) { } - public function render_notices_for_non_paid_events( $form ) { - - // participant processors - $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); - - if ( ! $processors ) return; - - array_map( function( $processor_id, $processor ) use( $form ) { - - // only for non paid events - if ( $processor['config']['is_monetary'] ) return; - - $this->get_notice( $processor_id, $form, $add_filter = true ); - - }, array_keys( $processors ), $processors ); - - } - /** * Get event. * @@ -566,23 +780,21 @@ public function default_status( $event, $config ) { * * @since 1.0 * @param array $form The form config + * @return array $registrations The participant registrations */ - public function is_registered_for( $form ) { + public function get_participant_registrations( $event_ids, $form ) { // participant processors $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); - - if ( ! $processors ) return; - + if ( ! $processors ) return false; // contact processors $contacts = $this->plugin->helper->get_processor_by_type( 'civicrm_contact', $form ); // return registrations if is set and only one contact, otherwise recalculate if ( count( $contacts ) == 1 && ! empty( $this->registrations ) ) return $this->registrations; - // cfc transient $transient = $this->plugin->transient->get(); - array_map( function( $processor ) use ( $transient ) { + return array_map( function( $processor ) use ( $transient, $event_ids ) { if ( ! isset( $processor['runtimes'] ) ) return; @@ -590,27 +802,21 @@ public function is_registered_for( $form ) { if ( ! isset( $transient->contacts->{$contact_link} ) || empty( $transient->contacts->{$contact_link} ) ) return; - $params = [ - 'contact_id' => $transient->contacts->{$contact_link}, - 'event_id' => [ 'IN' => array_values( $this->event_ids ) ] - ]; - try { - $participant = civicrm_api3( 'Participant', 'get', $params ); + $participant = civicrm_api3( 'Participant', 'get', [ + 'sequential' => 1, + 'contact_id' => $transient->contacts->{$contact_link}, + 'event_id' => [ 'IN' => array_values( $event_ids ) ] + ] ); } catch ( CiviCRM_API3_Exception $e ) { } - if ( $participant['count'] ) { - array_map( function( $participant ) use ( $processor ) { + if ( ! $participant['count'] ) return; - $event_id = $this->event_ids[$processor['ID']]; - - if ( $participant['event_id'] == $event_id ) - $this->registrations[$processor['ID']] = $participant; - - }, $participant['values'] ); - } + return array_pop( array_filter( $participant['values'], function( $participant ) use ( $processor, $event_ids ) { + return $participant['event_id'] == $event_ids[$processor['ID']]; + } ) ); }, $processors ); } From 686e455254dcae9c4a809ff2b4ef38a0886205d6 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Tue, 20 Nov 2018 21:49:48 +0000 Subject: [PATCH 05/68] always recalculate participant registrations --- processors/participant/class-participant-processor.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index 63d404d..c403262 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -787,10 +787,7 @@ public function get_participant_registrations( $event_ids, $form ) { // participant processors $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); if ( ! $processors ) return false; - // contact processors - $contacts = $this->plugin->helper->get_processor_by_type( 'civicrm_contact', $form ); - // return registrations if is set and only one contact, otherwise recalculate - if ( count( $contacts ) == 1 && ! empty( $this->registrations ) ) return $this->registrations; + // cfc transient $transient = $this->plugin->transient->get(); From 515f5dd702d3c82bcf870e5f86e189b70cafe7bd Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Tue, 20 Nov 2018 21:51:03 +0000 Subject: [PATCH 06/68] use has_field_type method --- fields/discount/class-civicrm-discount.php | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/fields/discount/class-civicrm-discount.php b/fields/discount/class-civicrm-discount.php index 7fdf18a..90a0496 100644 --- a/fields/discount/class-civicrm-discount.php +++ b/fields/discount/class-civicrm-discount.php @@ -81,26 +81,17 @@ public function register_field_type( $field_types ) { } /** - * Enqueue scripts + * Localize scripts * - * @since 0.4.4 - * + * @since 1.0 * @param array $form Form config * @return array $form Form config */ public function localize_scripts( $form ) { - $reference = false; - - foreach ( $form['fields'] as $field_id => $field ) { - if ( $field['type'] == $this->key_name ) { - $reference = true; - break; - } - } - - if( $reference ) { + + if ( Caldera_Forms_Field_Util::has_field_type( $this->key_name, $form ) ) wp_localize_script( 'cf-cividiscountjs', 'cfc', [ 'url' => admin_url( 'admin-ajax.php' ) ] ); - } + return $form; } From f1e473a1972d88f2dc1d8df92bdd03542f99fc9e Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 26 Nov 2018 03:40:56 +0000 Subject: [PATCH 07/68] make autopuplate price sets options available as conditionals --- assets/js/admin.js | 46 ++++++++++++++- assets/js/autopop_conditionals.js | 57 +++++++++++++++++++ .../class-civicrm-caldera-forms-assets.php | 13 +++++ 3 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 assets/js/autopop_conditionals.js diff --git a/assets/js/admin.js b/assets/js/admin.js index 61e431d..e3ab8c9 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -46,7 +46,7 @@ function cfc_select2_defaults( selector, value ) { } } - jQuery( selector ).cfcSelect2({ + jQuery( selector ).cfcSelect2({ ajax: { url: ajaxurl, dataType: 'json', @@ -102,5 +102,45 @@ function cfc_select2_defaults( selector, value ) { .append( new Option( e.params.data.text, e.params.data.id, false, false ) ) .trigger( 'select2:close' ); } ) - }); -} \ No newline at end of file + }); +} + +// decorate formJSON to add price field options for autopopulate field conditionals +jQuery( document ).ready( function( $ ) { + + $.fn.originalFormJSON = $.fn.formJSON; + + $.fn.formJSON = function() { + + var form = $( this ).originalFormJSON(); + + if ( ! form.config || ! form.config.fields ) return form; + + for ( var field_id in form.config.fields ) { + + var config = form.config.fields[field_id].config; + + if ( config.auto && config.auto_type.indexOf( 'price_field_' ) !== -1 ) { + + form.config.fields[field_id].config.option = {}; + + var price_field_id = config.auto_type.replace( 'cfc_', '' ), + options = preset_options[price_field_id].data; + + options.map( function( option ) { + + var parts = option.split( '|' ); + + form.config.fields[field_id].config.option[parts[0]] = { + value: parts[0], + label: parts[1], + calc_value: parts[2] + }; + + } ); + } + } + + return form; + } +} ); \ No newline at end of file diff --git a/assets/js/autopop_conditionals.js b/assets/js/autopop_conditionals.js new file mode 100644 index 0000000..3900436 --- /dev/null +++ b/assets/js/autopop_conditionals.js @@ -0,0 +1,57 @@ +// processor conditionals, add price field options for autopopulate +jQuery( document ).ready( function( $ ) { + $( '.caldera-editor-body' ).on( 'change', '.caldera-conditional-field-set', function( e ) { + + var field = $( this ), + field_compare = field.parent().find( '.compare-type' ), + type = field.data( 'condition' ), + pid = field.data( 'id' ), + name = "config[" + type + "][" + pid + "][conditions][group][" + field.data('row') + "][" + field.data('line') + "]", + lineid = field.data( 'line' ), + target = $( '#' + lineid + "_value" ), + curval = target.find( '.caldera-conditional-value-field' ).first(); + + var field_id = this.value, + form = core_form.formJSON(), + config = form.config.fields[field_id].config; + + if ( curval.length ) { + if ( curval.val().length ) + target.data( 'value', curval.val() ); + } else if ( 0 === target.val() ) { + target.data( 'value', 0 ); + } else if ( '0' === target.val() ) { + target.data( 'value', '0' ); + } + + field_compare.show(); + + if ( config.auto && config.auto_type.indexOf( 'price_field_' ) !== -1 ) { + var price_field = config.auto_type.replace( 'cfc_', '' ), + options_rows = preset_options[price_field].data, + out = ''; + + } + + target.html( out ); + + } ); +} ); \ No newline at end of file diff --git a/includes/class-civicrm-caldera-forms-assets.php b/includes/class-civicrm-caldera-forms-assets.php index 53ccd53..cb8d967 100644 --- a/includes/class-civicrm-caldera-forms-assets.php +++ b/includes/class-civicrm-caldera-forms-assets.php @@ -35,6 +35,8 @@ public function register_hooks() { // enqueue scripts and js in form editor add_action( 'caldera_forms_admin_assets_scripts_registered', [ $this, 'enqueue_civicrm_scripts' ] ); add_action( 'caldera_forms_admin_assets_styles_registered', [ $this, 'enqueue_civicrm_styles' ] ); + // enqueue late in editor + add_action( 'caldera_forms_editor_footer', [ $this, 'enqueue_in_editor_footer' ] ); } /** @@ -48,6 +50,8 @@ public function register_scripts_and_styles() { wp_register_style( 'cfc-select2', CF_CIVICRM_INTEGRATION_URL . 'assets/css/select2.min.css', [], CF_CIVICRM_INTEGRATION_VER ); // admin script wp_register_script( 'cfc-admin', CF_CIVICRM_INTEGRATION_URL . 'assets/js/admin.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); + // auto populate price field conditional options + wp_register_script( 'cfc-autopop-conditionals', CF_CIVICRM_INTEGRATION_URL . 'assets/js/autopop_conditionals.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); // frontend script wp_register_script( 'cfc-front', CF_CIVICRM_INTEGRATION_URL . 'assets/js/front.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); } @@ -85,6 +89,15 @@ public function enqueue_civicrm_styles(){ } + /** + * Enqueue conditional script in editor footer. + * + * @since 1.0 + */ + public function enqueue_in_editor_footer() { + wp_enqueue_script( 'cfc-autopop-conditionals' ); + } + /** * Check if are in Caldera Forms admin context. * From 40818ae18617a3f2f943bb952557eb16da2383ea Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 26 Nov 2018 10:13:44 +0000 Subject: [PATCH 08/68] abort if we don't have a field_id or it's config --- assets/js/autopop_conditionals.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/assets/js/autopop_conditionals.js b/assets/js/autopop_conditionals.js index 3900436..b2caece 100644 --- a/assets/js/autopop_conditionals.js +++ b/assets/js/autopop_conditionals.js @@ -12,8 +12,11 @@ jQuery( document ).ready( function( $ ) { curval = target.find( '.caldera-conditional-value-field' ).first(); var field_id = this.value, - form = core_form.formJSON(), - config = form.config.fields[field_id].config; + form = core_form.formJSON(); + + if ( ! field_id || ( ! form.config && ! form.config[field_id] ) ) return; + + var config = form.config.fields[field_id].config; if ( curval.length ) { if ( curval.val().length ) From 57d991285414b09a8213672972972fd2ee571984 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 26 Nov 2018 16:08:16 +0000 Subject: [PATCH 09/68] add support for premiums and premium field --- .../civicrm_premium/class-civicrm-premium.php | 150 ++++++++++++++++++ fields/civicrm_premium/config.php | 124 +++++++++++++++ fields/civicrm_premium/field.php | 116 ++++++++++++++ fields/civicrm_premium/js/premium.js | 39 +++++ fields/civicrm_premium/preview.php | 7 + includes/class-civicrm-caldera-forms-ajax.php | 23 ++- .../class-civicrm-caldera-forms-fields.php | 19 ++- processors/order/class-order-processor.php | 15 ++ processors/order/order_config.php | 8 + 9 files changed, 494 insertions(+), 7 deletions(-) create mode 100644 fields/civicrm_premium/class-civicrm-premium.php create mode 100644 fields/civicrm_premium/config.php create mode 100644 fields/civicrm_premium/field.php create mode 100644 fields/civicrm_premium/js/premium.js create mode 100644 fields/civicrm_premium/preview.php diff --git a/fields/civicrm_premium/class-civicrm-premium.php b/fields/civicrm_premium/class-civicrm-premium.php new file mode 100644 index 0000000..353f15b --- /dev/null +++ b/fields/civicrm_premium/class-civicrm-premium.php @@ -0,0 +1,150 @@ +plugin = $plugin; + // register Caldera Forms callbacks + $this->register_hooks(); + + } + + /** + * Register hooks. + * + * @since 1.0 + */ + public function register_hooks() { + + // add custom fields to Caldera UI + add_filter( 'caldera_forms_get_field_types', [ $this, 'register_field_type' ] ); + // add classes + add_filter( 'caldera_forms_render_field_classes_type-' . $this->key_name, [ $this, 'add_classes' ], 10, 3 ); + + add_filter( 'caldera_forms_render_get_field', [ $this, 'filter_field_config' ], 10, 2 ); + + } + + /** + * Adds the field definition for this field type to Caldera UI. + * + * @uses 'caldera_forms_get_field_types' filter + * + * @since 1.0 + * + * @param array $field_types The existing fields configuration + * @return array $field_types The modified fields configuration + */ + public function register_field_type( $field_types ) { + + $field_types[$this->key_name] = [ + 'field' => __( 'CiviCRM Premium', 'caldera-forms-civicrm' ), + 'file' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/field.php', + 'category' => __( 'CiviCRM', 'caldera-forms-civicrm' ), + 'description' => __( 'CiviCRM Premiums for Order processors', 'caldera-forms-civicrm' ), + 'setup' => [ + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/config.php', + 'preview' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/preview.php', + 'default' => [ + 'active_class' => 'btn-success', + 'default_class' => 'btn-default', + 'no_thanks' => 'No thank you' + ] + ], + // borough styles form CF's toggle_switch field + 'styles' => [ + CFCORE_URL . 'fields/toggle_switch/css/setup.css', + CFCORE_URL . 'fields/toggle_switch/css/toggle.css' + ], + 'scripts' => [ + CF_CIVICRM_INTEGRATION_URL . 'fields/civicrm_premium/js/premium.js' + ] + ]; + + return $field_types; + + } + + /** + * Add cf-toggle-switch class. + * + * @since 1.0 + * @param array $field_classes The classes array + * @param array $field The field config + * @param array $form The form config + */ + public function add_classes( $field_classes, $field, $form ) { + $field_classes['control_wrapper'][] = 'cf-toggle-switch'; + return $field_classes; + } + + /** + * Filter this field type and adds premium/product data to it's config. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @return array $field The filtered field + */ + public function filter_field_config( $field, $form ) { + + if ( $field['type'] != $this->key_name ) return $field; + + if ( isset( $field['config']['premium_id'] ) ) + $premium = civicrm_api3( 'Product', 'getsingle', [ 'id' => $field['config']['premium_id'] ] ); + + if ( ! $premium ) return $field; + + $premium_config = [ + 'name' => $premium['name'], + 'desc' => $premium['description'], + 'image' => $premium['image'] ? $premium['image'] : false, + 'thumbnail' => $premium['thumbnail'] ? $premium['thumbnail'] : false, + 'min' => sprintf( __( 'Minimum: %s', 'caldera-forms-civicrm' ), $premium['min_contribution'] ), + 'min_clean' => $premium['min_contribution'], + 'options' => $premium['options'] ? $this->to_array( $premium['options'] ) : false + ]; + + $field['config'] = array_merge( $field['config'], $premium_config ); + + return $field; + } + + /** + * Format product/premium options. + * + * @since 1.0 + * @param string $options Comma separated values + * @return array $options The options array + */ + public function to_array( string $options ) { + $options = preg_replace( '/\s+/', '', $options ); + return explode( ',', $options ); + } + +} diff --git a/fields/civicrm_premium/config.php b/fields/civicrm_premium/config.php new file mode 100644 index 0000000..aeaf258 --- /dev/null +++ b/fields/civicrm_premium/config.php @@ -0,0 +1,124 @@ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ {{{_field slug="calc" type="calculation"}}} +
+

+ +

+
+ + + + diff --git a/fields/civicrm_premium/field.php b/fields/civicrm_premium/field.php new file mode 100644 index 0000000..4b2ec15 --- /dev/null +++ b/fields/civicrm_premium/field.php @@ -0,0 +1,116 @@ + 0 ? 'disabled="true"' : 'disabled="false"'; ?> + + + +
+ + + +
+
+ +
+ <?php echo esc_attr( $field['config']['name'] ) ?> +
+ +
+
+

+ + +

+
+
+
+ +
+ +
+ + \ No newline at end of file diff --git a/fields/civicrm_premium/js/premium.js b/fields/civicrm_premium/js/premium.js new file mode 100644 index 0000000..a42fd25 --- /dev/null +++ b/fields/civicrm_premium/js/premium.js @@ -0,0 +1,39 @@ +jQuery( function( $ ) { + $( 'body' ).on( 'click', '.cf-toggle-group-premium a', function(){ + + var clicked = $( this ), + parent = clicked.closest( '.caldera-config-field' ), + input = parent.find( '[data-ref="' + clicked.attr( 'id' ) + '"]' ), + premium_wrapper = parent.find( '.premium-wrapper' ), + calc = parent.find( '.premium-calc' ); + + parent.find( '.btn' ).removeClass( clicked.data( 'active' ) ).addClass( clicked.data( 'default' ) ); + clicked.addClass( clicked.data( 'active' ) ).removeClass( clicked.data( 'default' ) ); + input.prop( 'checked', true ).trigger( 'change' ); + + if ( clicked.attr( 'id' ).indexOf( 'premium' ) !== -1 ) { + premium_wrapper.find( '.premium-mini' ).hide(); + premium_wrapper.find( '.premium-full' ).show(); + } else { + premium_wrapper.find( '.premium-mini' ).show(); + premium_wrapper.find( '.premium-full' ).hide(); + } + + } ); + + $( 'body' ).on( 'change', '[data-type="calculation"]', function( e ) { + var calc_field_id = $( this ).attr( 'data-calc-field' ), + min = $( '.premium' ).find( '[data-calc-field-id="' + calc_field_id + '"]' ), + toggles = $( '.cf-toggle-group-premium' ).find( '[data-field="' + min.attr( 'data-field-id' ) + '"]' ); + + if ( parseFloat( $( this ).val() ) >= parseFloat( min.val() ) ) { + toggles.map( function( index, element ) { + $( element ).attr( 'disabled', false ); + } ); + } else { + toggles.map( function( index, element ) { + $( element ).attr( 'disabled', true ); + } ); + } + } ); +} ); \ No newline at end of file diff --git a/fields/civicrm_premium/preview.php b/fields/civicrm_premium/preview.php new file mode 100644 index 0000000..9bef662 --- /dev/null +++ b/fields/civicrm_premium/preview.php @@ -0,0 +1,7 @@ +
+ {{#unless hide_label}}{{label}}{{#if required}} *{{/if}}{{/unless}} +
+ + {{caption}} +
+
\ No newline at end of file diff --git a/includes/class-civicrm-caldera-forms-ajax.php b/includes/class-civicrm-caldera-forms-ajax.php index 03925f0..47799cd 100644 --- a/includes/class-civicrm-caldera-forms-ajax.php +++ b/includes/class-civicrm-caldera-forms-ajax.php @@ -39,7 +39,8 @@ public function register_hooks() { // event code discount add_action( 'wp_ajax_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); add_action( 'wp_ajax_nopriv_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); - + // premiums + add_action( 'wp_ajax_civicrm_get_premiums', [ $this, 'civicrm_get_premiums' ] ); } /** @@ -179,4 +180,24 @@ public function do_code_cividiscount() { echo json_encode( $discounted_options ); die; } + + public function civicrm_get_premiums() { + if ( isset( $_POST['search'] ) ) $search_term = $_POST['search']; + if ( isset( $_POST['premium_id'] ) ) $premium_id = $_POST['premium_id']; + + if ( ! wp_verify_nonce( $_POST['nonce'], 'admin_get_premiums' ) ) return; + + $params = [ + 'sequential' => 1, + 'is_active' => 1, + ]; + + if ( isset( $premium_id ) ) $params['id'] = $premium_id; + if ( isset( $search_term ) && ! empty( $search_term ) ) $params['name'] = [ 'LIKE' => '%' . $search_term . '%' ]; + + $premiums = civicrm_api3( 'Product', 'get', $params ); + + echo json_encode( $premiums['values'] ); + die; + } } diff --git a/includes/class-civicrm-caldera-forms-fields.php b/includes/class-civicrm-caldera-forms-fields.php index 3f0d93f..dca8cf8 100644 --- a/includes/class-civicrm-caldera-forms-fields.php +++ b/includes/class-civicrm-caldera-forms-fields.php @@ -60,9 +60,13 @@ private function include_files() { // include civicrm field presets include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-core-fields-presets.php'; include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-custom-fields-presets.php'; - if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) + if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) { include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-price-sets-presets.php'; - include CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/class-civicrm-discount.php'; + if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) + include CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/class-civicrm-discount.php'; + // premium field + include CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/class-civicrm-premium.php'; + } } /** @@ -81,12 +85,15 @@ private function setup_objects() { // autopopulate and bulk insert/presets $this->presets_objects['civicrm_core_fields'] = new CiviCRM_Caldera_Forms_Core_Fields_Presets( $this->plugin ); $this->presets_objects['civicrm_custom_fields'] = new CiviCRM_Caldera_Forms_Custom_Fields_Presets( $this->plugin ); - if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) + if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) { $this->presets_objects['civicrm_price_sets'] = new CiviCRM_Caldera_Forms_Price_Sets_Presets( $this->plugin ); + // discount field for cividiscount integration + if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) + $this->field_objects['civicrm_discount'] = new CiviCRM_Caldera_Forms_Field_Discount( $this->plugin ); + // premium field + $this->field_objects['civicrm_premium'] = new CiviCRM_Caldera_Forms_Field_Premium( $this->plugin ); + } - // discount field for cividiscount integration - if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) - $this->field_objects['civicrm_discount'] = new CiviCRM_Caldera_Forms_Field_Discount( $this->plugin ); } } diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index a31c549..7bff469 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -216,6 +216,21 @@ public function processor( $config, $form, $processid ) { try { $create_order = civicrm_api3( 'Order', 'create', $form_values ); $this->order = $create_order; + + // create product + if ( isset( $form_values['product_id'] ) ) { + $params = [ + 'product_id' => $form_values['product_id'], + 'contribution_id' => $create_order['id'], + 'quantity' => 1 // FIXME, can this be set via UI? + ]; + + if ( isset( $transdata['data'][$config['product_id'] . '_option'] ) ) + $params['product_option'] = $transdata['data'][$config['product_id'] . '_option']; + + $create_premium = civicrm_api3( 'ContributionProduct', 'create', $params ); + + } } catch ( CiviCRM_API3_Exception $e ) { $transdata['error'] = true; $transdata['note'] = $e->getMessage() . '

getTraceAsString() . ''; diff --git a/processors/order/order_config.php b/processors/order/order_config.php index c7974b0..b789033 100644 --- a/processors/order/order_config.php +++ b/processors/order/order_config.php @@ -184,6 +184,14 @@ + +
+ +
+ {{{_field slug="product_id" type="civicrm_premium"}}} +
+
+
From 4f4301ba7a2c19c74bdc8e3288921a4099fe9d60 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Wed, 28 Nov 2018 03:10:08 +0000 Subject: [PATCH 10/68] refactor custom fields autopopulate/presets, and make them available as conditionals for both fields and processors --- assets/js/admin.js | 8 +- assets/js/autopop_conditionals.js | 9 +- .../class-civicrm-custom-fields-presets.php | 210 +++++++++++------- .../class-civicrm-price-sets-presets.php | 8 +- .../class-participant-processor.php | 14 ++ 5 files changed, 158 insertions(+), 91 deletions(-) diff --git a/assets/js/admin.js b/assets/js/admin.js index e3ab8c9..02b8be7 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -120,12 +120,12 @@ jQuery( document ).ready( function( $ ) { var config = form.config.fields[field_id].config; - if ( config.auto && config.auto_type.indexOf( 'price_field_' ) !== -1 ) { + if ( config.auto && ( config.auto_type.indexOf( 'price_field_' ) !== -1 || config.auto_type.indexOf( 'custom_' ) !== -1 ) ) { form.config.fields[field_id].config.option = {}; - - var price_field_id = config.auto_type.replace( 'cfc_', '' ), - options = preset_options[price_field_id].data; + // cfc_price_field_ or custom_ + var preset_name = config.auto_type.replace( 'cfc_', '' ), + options = preset_options[preset_name].data; options.map( function( option ) { diff --git a/assets/js/autopop_conditionals.js b/assets/js/autopop_conditionals.js index b2caece..9ec9bdb 100644 --- a/assets/js/autopop_conditionals.js +++ b/assets/js/autopop_conditionals.js @@ -29,9 +29,12 @@ jQuery( document ).ready( function( $ ) { field_compare.show(); - if ( config.auto && config.auto_type.indexOf( 'price_field_' ) !== -1 ) { - var price_field = config.auto_type.replace( 'cfc_', '' ), - options_rows = preset_options[price_field].data, + if ( config.auto && ( config.auto_type.indexOf( 'price_field_' ) !== -1 || config.auto_type.indexOf( 'custom_' ) !== -1 ) ) { + // cfc_price_field_ or custom_ + var preset_name = config.auto_type; + preset_name = config.auto_type.replace( 'cfc_', '' ); + + var options_rows = preset_options[preset_name].data, out = ' +
+ +

+ +
+ +
+ +
+
+
@@ -63,14 +71,6 @@
- -
- -
- -
-
-
@@ -105,6 +105,11 @@ $( '.fixed_price_field', $( price_field_value ) ).toggle( is_fixed ); } ).trigger( 'change' ); + $( entity_table + ' select' ).on( 'change', function( i, el ) { + var entity = $( this ).val(); + $( entity_data ).toggle( entity != 'civicrm_contribution' ); + } ).trigger( 'change' ); + $( is_other_amount + ' input' ).on( 'change', function( i, el ) { var checked = $( this ).prop( 'checked' ); $( amount ).toggle( checked ); From b0d48ad493bd5e23f0e61353d8279cbcc18f0407 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Wed, 2 Jan 2019 18:47:54 +0000 Subject: [PATCH 39/68] disable all fields options not only price set/price field fields --- .../class-civicrm-price-sets-presets.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index 9c94378..c542aa4 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -129,6 +129,10 @@ public function autopopulate_price_field_values( $field, $form ) { if ( current_filter() == 'caldera_forms_render_field_structure' ) $field = $this->filter_price_field_structure( $field, $form ); + // disable field options + if ( $this->disable_all_fields ) + $field = $this->disable_field_options( $field ); + if ( ! $this->is_price_field_field( $field, $form ) ) return $field; /** @@ -257,4 +261,25 @@ public function is_price_field_field( $field, $form ) { return true; } + /** + * Disable all field's options. + * + * @since 1.0 + * @param array $field The field config + * @return array $field The field config + */ + public function disable_field_options( $field ) { + + if ( ! isset( $field['config']['option'] ) ) return $field; + + array_map( function( $option_id ) use ( &$field ) { + + $field['config']['option'][$option_id]['disabled'] = $this->disable_all_fields; + + }, array_keys( $field['config']['option'] ) ); + + return $field; + + } + } From 7be13e26f7e3c60e0af4331a2402bfb0d18848a3 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Wed, 2 Jan 2019 18:55:08 +0000 Subject: [PATCH 40/68] typo --- processors/participant/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processors/participant/config.php b/processors/participant/config.php index 0e4b0a1..ad9d596 100644 --- a/processors/participant/config.php +++ b/processors/participant/config.php @@ -58,7 +58,7 @@

- main event.', 'caldera-forms-civicrm' ) ); ?> + main event.', 'caldera-forms-civicrm' ) ); ?>

From 789f8a048ef19ad5cf9677dd347f83d6ddab9882 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 7 Jan 2019 18:50:50 +0000 Subject: [PATCH 41/68] disable 'mapped' price fields we know --- fields/presets/class-civicrm-price-sets-presets.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index c542aa4..572dbbd 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -131,7 +131,7 @@ public function autopopulate_price_field_values( $field, $form ) { // disable field options if ( $this->disable_all_fields ) - $field = $this->disable_field_options( $field ); + $field = $this->disable_all_price_field_options( $field ); if ( ! $this->is_price_field_field( $field, $form ) ) return $field; @@ -268,10 +268,12 @@ public function is_price_field_field( $field, $form ) { * @param array $field The field config * @return array $field The field config */ - public function disable_field_options( $field ) { + public function disable_all_price_field_options( $field ) { if ( ! isset( $field['config']['option'] ) ) return $field; + if ( ! in_array( $field['ID'], $this->plugin->processors->processors['participant']->price_field_refs ) ) return $field; + array_map( function( $option_id ) use ( &$field ) { $field['config']['option'][$option_id]['disabled'] = $this->disable_all_fields; From 73d05053a668d52b48bbbb54f4394792fa8eb2f7 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 7 Jan 2019 22:15:09 +0000 Subject: [PATCH 42/68] prevent php warnings/notices --- includes/class-civicrm-caldera-forms-cividiscount.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/includes/class-civicrm-caldera-forms-cividiscount.php b/includes/class-civicrm-caldera-forms-cividiscount.php index d49fc6d..b82b126 100644 --- a/includes/class-civicrm-caldera-forms-cividiscount.php +++ b/includes/class-civicrm-caldera-forms-cividiscount.php @@ -100,7 +100,11 @@ public function get_cividiscounts() { * @return array $cividiscounts */ public function get_cividiscounts_by_entity( $entity_name, $is_autodiscount = null ) { + $discounts = $this->get_cividiscounts(); + + if ( ! $discounts ) return; + return array_filter( $discounts, function( $discount ) use ( $entity_name, $is_autodiscount ) { if ( $is_autodiscount === true ) { return array_key_exists( $entity_name, $discount ) && ! empty( $discount['autodiscount'] ); @@ -110,6 +114,7 @@ public function get_cividiscounts_by_entity( $entity_name, $is_autodiscount = nu return array_key_exists( $entity_name, $discount ); } } ); + } /** @@ -125,6 +130,8 @@ public function get_event_cividiscounts( $event_ids ) { $event_cividiscounts = $this->get_cividiscounts_by_entity( 'events' ); + if ( ! $event_discounts ) return; + $event_discounts = array_reduce( $event_ids, function( $discounts, $event_id ) use ( $event_ids, $event_cividiscounts ) { $processor_id = array_search( $event_id, $event_ids ); @@ -156,6 +163,8 @@ public function build_options_ids_refs( $price_field_refs, $form = false ) { $discounted_options = $this->get_cividiscounts_by_entity( 'pricesets' ); + if ( ! $discounted_options ) return; + $options_ids_refs = array_reduce( $price_field_refs, function( $refs, $field_id ) use ( $price_field_refs, $discounted_options, $form ) { $processor_id = array_search( $field_id, $price_field_refs ); @@ -194,6 +203,8 @@ public function build_options_ids_refs( $price_field_refs, $form = false ) { */ public function get_options_cividiscounts( $options_ids_refs ) { + if ( ! $options_ids_refs ) return; + if ( is_array( $this->options_cividiscounts ) && ! empty( $this->options_cividiscounts ) ) return $this->options_cividiscounts; $options_cividiscounts = $this->get_cividiscounts_by_entity( 'pricesets' ); From fb40644d6294dfd2f09c1d4820283a638cc82694 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 10 Jan 2019 02:14:47 +0000 Subject: [PATCH 43/68] fix cividiscount: mutate state and rebind discounted fields --- fields/discount/field.php | 6 +++++- fields/discount/js/cividiscount.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/fields/discount/field.php b/fields/discount/field.php index 6ee4e98..ca0930f 100644 --- a/fields/discount/field.php +++ b/fields/discount/field.php @@ -27,7 +27,8 @@ class="btn btn-block" var code = $( '#' ).val(), discount_button = $( '#_cividiscount_button' ), form_id = '', - form_id_attr = ''; + form_id_attr = '', + form_instance = ''; if ( ! code ) return; @@ -51,6 +52,7 @@ class="btn btn-block" element = $( '#' + option_id ); // calc value element.attr( 'data-calc-value', option.calc_value ); + element.data( 'calc-value', option.calc_value ); // label text element.parent().attr( 'data-label', option.label ); element.parent().contents().map( function( el ) { @@ -65,6 +67,8 @@ class="btn btn-block" } + $( 'body' ).trigger( 'cfc.discount.apply', { form_id: form_id_attr, instance: form_instance, options: options } ); + } } ); diff --git a/fields/discount/js/cividiscount.js b/fields/discount/js/cividiscount.js index e69de29..f23e764 100644 --- a/fields/discount/js/cividiscount.js +++ b/fields/discount/js/cividiscount.js @@ -0,0 +1,16 @@ +jQuery( document ).on( 'cfc.discount.apply', function ( event, data ) { + + var state = cfstate[data.form_id]; + + for ( option_id in data.options ) { + + var option = data.options[option_id], + field_id = option.field_id + '_' + data.instance; + + state.mutateState( field_id, option.value ); + + state.rebind( field_id ); + + } + +} ); From 896755f3828f5912a7ca09f3df41383df0e72a6b Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 10 Jan 2019 02:15:49 +0000 Subject: [PATCH 44/68] bump version --- caldera-forms-civicrm.php | 4 ++-- readme.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/caldera-forms-civicrm.php b/caldera-forms-civicrm.php index 18a2597..457177a 100644 --- a/caldera-forms-civicrm.php +++ b/caldera-forms-civicrm.php @@ -2,7 +2,7 @@ /** * Plugin Name: Caldera Forms CiviCRM * Description: CiviCRM integration for Caldera Forms. - * Version: 1.0 + * Version: 1.0.1 * Author: Andrei Mondoc * Author URI: https://github.com/mecachisenros * Plugin URI: https://github.com/mecachisenros/caldera-forms-civicrm @@ -16,7 +16,7 @@ * * @since 0.1 */ -define( 'CF_CIVICRM_INTEGRATION_VER', '1.0' ); +define( 'CF_CIVICRM_INTEGRATION_VER', '1.0.1' ); define( 'CF_CIVICRM_INTEGRATION_URL', plugin_dir_url( __FILE__ ) ); define( 'CF_CIVICRM_INTEGRATION_PATH', plugin_dir_path( __FILE__ ) ); diff --git a/readme.txt b/readme.txt index ca43f2a..0266521 100755 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: mecachisenros, needle Tags: civicrm, caldera, forms, integration Requires at least: 4.5 Tested up to: 4.9.8 -Stable tag: 1.0 +Stable tag: 1.0.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html From d65a55ff133908a6b86e0d10873393f4117fbb87 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Tue, 15 Jan 2019 11:42:21 +0000 Subject: [PATCH 45/68] abort if not array --- a.out | 0 assets/js/autopop_conditionals.js | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) delete mode 100644 a.out diff --git a/a.out b/a.out deleted file mode 100644 index e69de29..0000000 diff --git a/assets/js/autopop_conditionals.js b/assets/js/autopop_conditionals.js index 7770306..2b63935 100644 --- a/assets/js/autopop_conditionals.js +++ b/assets/js/autopop_conditionals.js @@ -40,6 +40,8 @@ jQuery( document ).ready( function( $ ) { out = ''; - } + target.html( out ); - target.html( out ); + } } ); } ); \ No newline at end of file From 1a78c047b89287ea587fdf9792037ba12a28ac3d Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Tue, 15 Jan 2019 21:07:57 +0000 Subject: [PATCH 46/68] prevent undefined index/variable notices --- fields/civicrm_premium/field.php | 4 ++-- .../presets/class-civicrm-custom-fields-presets.php | 2 +- fields/presets/class-civicrm-price-sets-presets.php | 2 +- .../class-civicrm-caldera-forms-cividiscount.php | 12 +++++++----- includes/class-civicrm-caldera-forms-forms.php | 6 +++--- .../participant/class-participant-processor.php | 2 +- templates/notices.php | 6 +++--- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/fields/civicrm_premium/field.php b/fields/civicrm_premium/field.php index 0efc888..2233c27 100644 --- a/fields/civicrm_premium/field.php +++ b/fields/civicrm_premium/field.php @@ -22,7 +22,7 @@ class="btn " title=""> - + - + required="required" type="radio" diff --git a/fields/presets/class-civicrm-custom-fields-presets.php b/fields/presets/class-civicrm-custom-fields-presets.php index 7235152..40fdee4 100644 --- a/fields/presets/class-civicrm-custom-fields-presets.php +++ b/fields/presets/class-civicrm-custom-fields-presets.php @@ -158,7 +158,7 @@ public function autopopulate_custom_fields_types() { */ public function autopopulate_custom_fields_values( $field, $form ) { - if ( ! $field['config']['auto'] ) return $field; + if ( ! isset( $field['config']['auto'] ) ) return $field; if ( strpos( $field['config']['auto_type'], 'custom_' ) === false ) return $field; diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index 572dbbd..9124149 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -154,7 +154,7 @@ public function autopopulate_price_field_values( $field, $form ) { 'disabled' => $this->disable_all_fields ]; - if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + if ( isset( $price_field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) { $option['calc_value'] += $price_field_value['tax_amount']; $option['label'] = $this->plugin->helper->format_tax_label( $price_field_value['label'], $price_field_value['amount'], $price_field_value['tax_amount'] ); } diff --git a/includes/class-civicrm-caldera-forms-cividiscount.php b/includes/class-civicrm-caldera-forms-cividiscount.php index b82b126..c99cdb7 100644 --- a/includes/class-civicrm-caldera-forms-cividiscount.php +++ b/includes/class-civicrm-caldera-forms-cividiscount.php @@ -130,7 +130,7 @@ public function get_event_cividiscounts( $event_ids ) { $event_cividiscounts = $this->get_cividiscounts_by_entity( 'events' ); - if ( ! $event_discounts ) return; + if ( ! isset( $event_discounts ) ) return; $event_discounts = array_reduce( $event_ids, function( $discounts, $event_id ) use ( $event_ids, $event_cividiscounts ) { @@ -163,7 +163,7 @@ public function build_options_ids_refs( $price_field_refs, $form = false ) { $discounted_options = $this->get_cividiscounts_by_entity( 'pricesets' ); - if ( ! $discounted_options ) return; + if ( ! isset( $discounted_options ) ) return; $options_ids_refs = array_reduce( $price_field_refs, function( $refs, $field_id ) use ( $price_field_refs, $discounted_options, $form ) { @@ -203,12 +203,14 @@ public function build_options_ids_refs( $price_field_refs, $form = false ) { */ public function get_options_cividiscounts( $options_ids_refs ) { - if ( ! $options_ids_refs ) return; + if ( ! isset( $options_ids_refs ) ) return; if ( is_array( $this->options_cividiscounts ) && ! empty( $this->options_cividiscounts ) ) return $this->options_cividiscounts; $options_cividiscounts = $this->get_cividiscounts_by_entity( 'pricesets' ); + if ( ! isset( $options_cividiscounts ) ) return; + $discounts = []; array_map( function( $field_id, $options ) use ( &$discounts, $options_ids_refs, $options_cividiscounts ) { @@ -335,7 +337,7 @@ public function do_discounted_option( $option, $field, $price_field_value, $disc }, 10, 2 ); // has tax - if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + if ( isset( $price_field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) { $option['calc_value'] += $price_field_value['tax_amount']; $option['label'] = $this->plugin->helper->format_tax_label( $label, $discounted_amount, $price_field_value['tax_amount'] ); } @@ -353,7 +355,7 @@ public function do_discounted_option( $option, $field, $price_field_value, $disc */ public function do_code_discount( $discount, $form ) { - if ( ! $discount ) return; + if ( ! isset( $discount ) || empty( $discount ) ) return; $options = $this->do_code_event_discount_options( $discount, $form ); $options = $this->do_code_options_discount_options( $discount, $form ); diff --git a/includes/class-civicrm-caldera-forms-forms.php b/includes/class-civicrm-caldera-forms-forms.php index 1e4718f..96bca12 100644 --- a/includes/class-civicrm-caldera-forms-forms.php +++ b/includes/class-civicrm-caldera-forms-forms.php @@ -194,7 +194,7 @@ public function render_notices_template( $form ) { */ $template_path = apply_filters( 'cfc_notices_template_path', CF_CIVICRM_INTEGRATION_PATH . 'templates/notices.php', $form ); - $html = $this->plugin->html->generate( $form, $template_path ); + $html = $this->plugin->html->generate( [ 'form' => $form ], $template_path ); // output template echo $html; } @@ -325,9 +325,9 @@ public function rebuild_calculation_field_formula( $field, $form ) { // rebuild formula foreach ( $field['config']['config']['group'] as $gid => $group ) { - $formula .= ! $group_id ? + $formula .= ! $gid ? $do_group_lines( $group['lines'] ) - : ( $group['operator'] ? + : ( isset( $group['operator'] ) ? ' ' . $group['operator'] . ' ' : $do_group_lines( $group['lines'] ) ); diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index 30992d4..b991991 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -435,7 +435,7 @@ public function filter_price_field_config( $field, $form, $price_field, $current $field['required'] = 0; // set disable all fields flag - if ( $processor['config']['disable_all_fields'] ) $this->plugin->fields->presets_objects['civicrm_price_sets']->disable_all_fields = true; + if ( isset( $processor['config']['disable_all_fields'] ) ) $this->plugin->fields->presets_objects['civicrm_price_sets']->disable_all_fields = true; } diff --git a/templates/notices.php b/templates/notices.php index a03c3b4..4a5e1bd 100644 --- a/templates/notices.php +++ b/templates/notices.php @@ -5,14 +5,14 @@ * * Contains a reference to the form configuration. * @since 1.0 - * @param array $data The form config + * @param array $data The data array * @param object $plugin Reference to this plugin */ -$notices = apply_filters( 'cfc_notices_to_render', [], $form, $plugin ); +$notices = apply_filters( 'cfc_notices_to_render', [], $data, $plugin ); ?> -
+
From cad7d2e5b9cf38adbee9ac0f8100b25078f499cd Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 18 Jan 2019 16:20:15 +0000 Subject: [PATCH 47/68] fix possible duplicate org for contact.current_employer (Contact Reference Field) --- .../class-civicrm-contact-reference.php | 47 ++++++++++++------- .../contact/class-contact-processor.php | 8 ++++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php index 5ee754a..796f854 100644 --- a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php +++ b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php @@ -101,21 +101,26 @@ public function register_field_type( $field_types ) { */ public function handle_current_employer_field( $mapped_field, $civi_field, $field, $config, $form ) { - if ( $civi_field == 'current_employer' && $field['type'] == 'civicrm_contact_reference' ) { - if ( ! is_numeric( $mapped_field ) && isset( $field['config']['new_organization'] ) ) { - $employer = civicrm_api3( 'Contact', 'create', [ - 'contact_type' => 'Organization', - 'organization_name' => $mapped_field, - ] ); - } else { - $employer = civicrm_api3( 'Contact', 'get', [ - 'contact_id' => $mapped_field, - 'return' => 'organization_name' - ] ); - } - return $employer['values'][$employer['id']]['organization_name']; + if ( $civi_field != 'current_employer' && $field['type'] != 'civicrm_contact_reference' ) return $mapped_field; + + if ( ! is_numeric( $mapped_field ) && isset( $field['config']['new_organization'] ) ) { + $employer = civicrm_api3( 'Contact', 'create', [ + 'contact_type' => 'Organization', + 'organization_name' => $mapped_field, + ] ); + } else { + $employer = civicrm_api3( 'Contact', 'get', [ + 'contact_id' => $mapped_field, + 'return' => 'organization_name' + ] ); } + if ( isset( $employer['count'] ) && $employer['count'] ) + return [ + 'organization_name' => $employer['values'][$employer['id']]['organization_name'], + 'employer_id' => $employer['id'] + ]; + return $mapped_field; } @@ -131,10 +136,18 @@ public function handle_current_employer_field( $mapped_field, $civi_field, $fiel * @param array $config processor config */ public function pre_render_current_employer_value( $value, $civi_field, $field, $entity, $config ) { - if ( $civi_field == 'current_employer' && $field['type'] == 'civicrm_contact_reference' ) { - $employer = civicrm_api3( 'Contact', 'get', [ 'contact_type' => 'Organization', 'organization_name' => $entity[$civi_field] ] ); - return $employer['id']; - } + + if ( $civi_field != 'current_employer' && $field['type'] != 'civicrm_contact_reference' ) return $value; + + $employer = civicrm_api3( 'Contact', 'get', [ + 'contact_type' => 'Organization', + 'organization_name' => $entity[$civi_field], + 'return' => 'organization_name', + 'options' => [ 'limit' => 1 ] + ] ); + + if ( isset( $employer['count'] ) && $employer['count'] ) return $employer['id']; + return $value; } diff --git a/processors/contact/class-contact-processor.php b/processors/contact/class-contact-processor.php index 014b918..dde0513 100644 --- a/processors/contact/class-contact-processor.php +++ b/processors/contact/class-contact-processor.php @@ -197,6 +197,14 @@ public function pre_processor( $config, $form, $processid ) { $form_values['civicrm_contact']['image_URL'] = CRM_Utils_System::url( 'civicrm/contact/imagefile', ['photo' => $file['uri']], true ); } + // contact reference field for organization maps to an array [ 'organization_name' => , 'employer_id' => ] + if ( ! empty( $form_values['civicrm_contact']['current_employer'] ) && is_array( $form_values['civicrm_contact']['current_employer'] ) ) { + $org = $form_values['civicrm_contact']['current_employer']; + $form_values['civicrm_contact']['current_employer'] = $org['organization_name']; + // need to be set in case of duplicate orgs with same name + $form_values['civicrm_contact']['employer_id'] = $org['employer_id']; + } + try { $create_contact = civicrm_api3( 'Contact', 'create', $form_values['civicrm_contact'] ); } catch ( CiviCRM_API3_Exception $e ) { From 06f49005621ca5de380d976155bba40deefed264 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 18 Jan 2019 19:29:06 +0000 Subject: [PATCH 48/68] fix condition --- .../class-civicrm-contact-reference.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php index 796f854..cd034b0 100644 --- a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php +++ b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php @@ -137,7 +137,9 @@ public function handle_current_employer_field( $mapped_field, $civi_field, $fiel */ public function pre_render_current_employer_value( $value, $civi_field, $field, $entity, $config ) { - if ( $civi_field != 'current_employer' && $field['type'] != 'civicrm_contact_reference' ) return $value; + if ( $field['type'] != 'civicrm_contact_reference' ) return $value; + + if ( $civi_field != 'current_employer' ) return $value; $employer = civicrm_api3( 'Contact', 'get', [ 'contact_type' => 'Organization', From 257b088dd6221fe0e61f00958ebe7e9ab85ed86d Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 18 Jan 2019 20:59:24 +0000 Subject: [PATCH 49/68] fix condition when mapping as well, duh! --- .../class-civicrm-contact-reference.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php index cd034b0..6f8133d 100644 --- a/fields/civicrm_contact_reference/class-civicrm-contact-reference.php +++ b/fields/civicrm_contact_reference/class-civicrm-contact-reference.php @@ -101,7 +101,9 @@ public function register_field_type( $field_types ) { */ public function handle_current_employer_field( $mapped_field, $civi_field, $field, $config, $form ) { - if ( $civi_field != 'current_employer' && $field['type'] != 'civicrm_contact_reference' ) return $mapped_field; + if ( $field['type'] != 'civicrm_contact_reference' ) return $mapped_field; + + if ( $civi_field != 'current_employer' ) return $mapped_field; if ( ! is_numeric( $mapped_field ) && isset( $field['config']['new_organization'] ) ) { $employer = civicrm_api3( 'Contact', 'create', [ From da5297be9208cee668e9a0b2cf77b06df5df18c4 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 21 Jan 2019 11:57:17 +0000 Subject: [PATCH 50/68] prevent undefined index/variable notices --- processors/line-item/class-line-item-processor.php | 11 ----------- .../participant/class-participant-processor.php | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index 77e368e..7d287bd 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -16,15 +16,6 @@ class CiviCRM_Caldera_Forms_Line_Item_Processor { */ public $plugin; - /** - * Contact link. - * - * @since 0.4.4 - * @access protected - * @var string $contact_link The contact link - */ - protected $contact_link; - /** * The processor key. * @@ -87,8 +78,6 @@ public function pre_processor( $config, $form, $processid ) { public function processor( $config, $form, $processid ) { $transient = $this->plugin->transient->get(); - - $this->contact_link = 'cid_' . $config['contact_link']; // price field value params aka 'line_item' $price_field_value = isset( $config['is_fixed_price_field'] ) ? diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index b991991..69650b3 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -161,7 +161,7 @@ public function pre_processor( $config, $form, $processid ) { $transient->participants->{$config['processor_id']}->params = $form_values; $this->plugin->transient->save( $transient->ID, $transient ); - if ( $config['is_email_receipt'] ) { + if ( isset( $config['is_email_receipt'] ) ) { add_action( 'cfc_order_post_processor', function( $order, $order_config, $form, $processid ) use ( $event, $config ) { From 224677807dbed9f556f9747a0ef2f9d5dd1bf29d Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Mon, 21 Jan 2019 11:58:43 +0000 Subject: [PATCH 51/68] catch exception when retrieving contact_id --- includes/class-civicrm-caldera-forms-helper.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/class-civicrm-caldera-forms-helper.php b/includes/class-civicrm-caldera-forms-helper.php index bc4141e..2d74bb0 100644 --- a/includes/class-civicrm-caldera-forms-helper.php +++ b/includes/class-civicrm-caldera-forms-helper.php @@ -165,7 +165,12 @@ public function get_wp_civi_contact( $id ) { 'domain_id' => CRM_Core_BAO_Domain::getDomain()->id, ]; - $wp_civicrm_contact = civicrm_api3( 'UFMatch', 'getsingle', $params ); + try { + $wp_civicrm_contact = civicrm_api3( 'UFMatch', 'getsingle', $params ); + } catch ( CiviCRM_API3_Exception $e ) { + Civi::log()->debug( 'Unable to match contact for user with id ' . $id ); + } + return $wp_civicrm_contact['contact_id']; } From 616a97d23fce940716334068835ee997d314a4c4 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 25 Jan 2019 13:37:57 +0000 Subject: [PATCH 52/68] possibility to allocate line_items to a participant/participants --- .../class-civicrm-caldera-forms-helper.php | 3 +- .../line-item/class-line-item-processor.php | 16 +- processors/line-item/line_item_config.php | 5 - processors/order/class-order-processor.php | 405 ++++++++++++------ .../class-participant-processor.php | 2 +- 5 files changed, 291 insertions(+), 140 deletions(-) diff --git a/includes/class-civicrm-caldera-forms-helper.php b/includes/class-civicrm-caldera-forms-helper.php index 2d74bb0..85e9378 100644 --- a/includes/class-civicrm-caldera-forms-helper.php +++ b/includes/class-civicrm-caldera-forms-helper.php @@ -623,6 +623,7 @@ public function get_tax_rates() { 'financial_account_id.financial_account_type_id', 'financial_account_id.tax_rate' ], + 'financial_account_id.is_active' => 1, 'financial_account_id.is_tax' => 1, 'options' => [ 'limit' => 0 ] ] ); @@ -739,7 +740,7 @@ public function get_price_sets() { $price_field_values = []; foreach ( $all_price_field_values['values'] as $id => $price_field_value ) { - $price_field_value['amount'] = number_format( $price_field_value['amount'], 2, '.', '' ); + $price_field_value['amount'] = $price_field_value['amount']; $price_field_values[$id] = $price_field_value; } diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index 7d287bd..e08a9bb 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -172,7 +172,10 @@ public function process_membership( $config, $form, $transient, $price_field_val $entity_params['is_price_field_based'] ); + $transient->memberships->$processor_id->params = $entity_params; + $line_item = [ + 'processor_entity' => $processor_id, 'line_item' => $price_field_value, 'params' => $entity_params ]; @@ -231,7 +234,10 @@ public function process_participant( $config, $form, $transient, $price_field_va $entity_params['is_price_field_based'] ); + $transient->participants->$processor_id->params = $entity_params; + $line_item = [ + 'processor_entity' => $processor_id, 'line_item' => $price_field_value, 'params' => $entity_params ]; @@ -259,8 +265,12 @@ public function process_contribution( $config, $form, $transient, $price_field_v $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); $price_field_value[0]['line_total'] = $price_field_value[0]['unit_price'] = $price_field_value[0]['amount'] = $form_values['amount']; } - + + if ( isset( $config['entity_params'] ) && ! empty( $config['entity_params'] ) ) + $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); + $line_item = [ + 'processor_entity' => isset( $processor_id ) ? $processor_id : false, 'line_item' => $price_field_value ]; @@ -294,10 +304,6 @@ public function build_price_field_values_array( $price_field_value, $entity_tabl $field_value['entity_table'] = $entity_table ? $entity_table : $this->guess_entity_table( $price_field_value ); - // add tax amount - if ( isset( $field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) - $field_value['amount'] += $field_value['tax_amount']; - unset( $field_value['id'], $field_value['name'], diff --git a/processors/line-item/line_item_config.php b/processors/line-item/line_item_config.php index ad61e5b..f3838ed 100644 --- a/processors/line-item/line_item_config.php +++ b/processors/line-item/line_item_config.php @@ -105,11 +105,6 @@ $( '.fixed_price_field', $( price_field_value ) ).toggle( is_fixed ); } ).trigger( 'change' ); - $( entity_table + ' select' ).on( 'change', function( i, el ) { - var entity = $( this ).val(); - $( entity_data ).toggle( entity != 'civicrm_contribution' ); - } ).trigger( 'change' ); - $( is_other_amount + ' input' ).on( 'change', function( i, el ) { var checked = $( this ).prop( 'checked' ); $( amount ).toggle( checked ); diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index e0ee88b..fb7588f 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -43,6 +43,15 @@ class CiviCRM_Caldera_Forms_Order_Processor { */ public $is_pay_later; + /** + * Total tax amount. + * + * @since 1.0.1 + * @access public + * @var float $total_tax_amount + */ + public $total_tax_amount = 0; + /** * The order result. * @@ -132,9 +141,6 @@ public function processor( $config, $form, $processid ) { $transient = $this->plugin->transient->get(); $this->contact_link = 'cid_' . $config['contact_link']; - $config_line_items = $config['line_items']; - unset( $config['line_items'] ); - // Get form values $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); @@ -146,8 +152,6 @@ public function processor( $config, $form, $processid ) { $form_values['currency'] = $config['currency']; - // $form_values['receipt_date'] = date( 'YmdHis' ); - // contribution page for reciepts if ( isset( $config['contribution_page_id'] ) ) $form_values['contribution_page_id'] = $config['contribution_page_id']; @@ -167,40 +171,14 @@ public function processor( $config, $form, $processid ) { $form_values['contact_id'] = $transient->contacts->{$this->contact_link}; // line items - $line_items = []; - $count = 0; - $total_tax_amount = 0; - foreach ( $config_line_items as $item => $processor ) { - if( ! empty( $processor ) ) { - $processor = Caldera_Forms::do_magic_tags( $processor ); - // line item is enabled and is not empty - if ( ! strpos( $processor, 'civicrm_line_item' ) && ! empty( ( array ) $transient->line_items->$processor ) ) { - $line_items[$count] = $transient->line_items->$processor->params; - // tax amount - if ( isset( $line_items[$count]['line_item'][0]['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) $total_tax_amount += $line_items[$count]['line_item'][0]['tax_amount']; - // membership is pay later - if ( isset( $line_items[$count]['params']['membership_type_id'] ) && $this->is_pay_later ) { - // set membership as pending - $line_items[$count]['params']['status_id'] = 'Pending'; - $line_items[$count]['params']['is_override'] = 1; - } - // participant is pay later - if ( isset( $line_items[$count]['params']['event_id'] ) && $this->is_pay_later ) { - // set participant as pending - $line_items[$count]['params']['status_id'] = 'Pending from pay later'; - } - $count++; - } - } else { - unset( $config_line_items[$item] ); - } - } + $line_items = $this->build_line_items_params( $transient, $config, $form ); - // add total tax amount - if ( $total_tax_amount ) - $form_values['tax_amount'] = $total_tax_amount; + if ( $this->has_participant_item( $line_items ) ) + $line_items = $this->maybe_format_line_items_to_entity( $line_items, $config, $form ); - $form_values['line_items'] = $line_items; + // add tax amount + if ( $this->total_tax_amount ) + $form_values['tax_amount'] = $this->total_tax_amount; // stripe metadata if ( $this->charge_metadata ) $form_values = array_merge( $form_values, $this->charge_metadata ); @@ -219,25 +197,17 @@ public function processor( $config, $form, $processid ) { $form_values = array_merge( $form_values, $metadata ); } + $form_values['line_items'] = $line_items; + try { $create_order = civicrm_api3( 'Order', 'create', $form_values ); $this->order = ( $create_order['count'] && ! $create_order['is_error'] ) ? $create_order['values'][$create_order['id']] : false; // create product - if ( isset( $form_values['product_id'] ) ) { - $params = [ - 'product_id' => $form_values['product_id'], - 'contribution_id' => $create_order['id'], - 'quantity' => 1 // FIXME, can this be set via UI? - ]; + if ( $this->order ) + $this->create_premium( $this->order, $form_values, $config ); - if ( isset( $transdata['data'][$config['product_id'] . '_option'] ) ) - $params['product_option'] = $transdata['data'][$config['product_id'] . '_option']; - - $create_premium = civicrm_api3( 'ContributionProduct', 'create', $params ); - - } } catch ( CiviCRM_API3_Exception $e ) { $transdata['error'] = true; $transdata['note'] = $e->getMessage() . '

getTraceAsString() . ''; @@ -264,7 +234,13 @@ public function post_processor( $config, $form, $processid ) { $transient = $this->plugin->transient->get(); // preserve join dates - $this->preserve_membership_join_date( $config, $form, $processid ); + $this->preserve_membership_join_date( $form ); + + $line_items = civicrm_api3( 'LineItem', 'get', [ + 'contribution_id' => $this->order['id'] + ] ); + + $this->order = array_merge( $this->order, [ 'line_items' => $line_items['values'] ] ); if ( true ) { //$config['is_thank_you'] ) { add_filter( 'caldera_forms_ajax_return', function( $out, $_form ) use ( $transdata, $transient ){ @@ -283,6 +259,7 @@ public function post_processor( $config, $form, $processid ) { $data = [ 'values' => $form_values, + 'order' => $this->order, 'form' => $_form, 'transdata' => $transdata, 'transient' => $transient @@ -297,106 +274,141 @@ public function post_processor( $config, $form, $processid ) { }, 10, 2 ); } + /** + * Runs when Order processor is post_processed if an order has been created. + * + * @since 1.0 + * @param array $order The created order result + * @param array $config The processor config + * @param array $form The form config + * @param string $processid The process id + */ + do_action( 'cfc_order_post_processor', $order, $config, $form, $processid ); + // send confirmation/receipt $this->maybe_send_confirmation( $this->order, $config ); - if ( $this->order ) { + } - $line_items = civicrm_api3( 'LineItem', 'get', [ - 'contribution_id' => $this->order['id'] - ] ); + /** + * Builds line items parameters array formatted for Order.create. + * + * @since 1.0.1 + * @param object $transient The Caldera Forms CiviCRM transient object + * @param array $config The processor config + * @param array $form The form config + * @return array $line_items The formatted line items array + */ + public function build_line_items_params( $transient, $config, $form ) { - $order = array_merge( $this->order, [ 'line_items' => $line_items['values'] ] ); + if ( empty( $config['line_items'] ) ) return []; - // FIXME - // figure out a better way to handle this - // although here's the only place where we know if we have participants or not - // - // record/track cividiscounts - if ( isset( $this->plugin->cividiscount ) && ! empty( $this->plugin->processors->processors['participant']->discounts_used ) && ( ! empty( $this->plugin->processors->processors['participant']->price_field_refs ) || ! empty( $this->plugin->processors->processors['participant']->price_field_option_refs ) ) ) { + return array_reduce( $config['line_items'], function( $line_items, $item_processor_tag ) use ( $transient, $form ) { - $price_field_refs = $this->plugin->processors->processors['participant']->price_field_refs; - $price_field_option_refs = $this->plugin->processors->processors['participant']->price_field_option_refs; - $discounts_used = $this->plugin->processors->processors['participant']->discounts_used; + if ( empty( $item_processor_tag ) ) return $line_items; - $price_field_option_refs = array_reduce( $price_field_option_refs, function( $refs, $ref ) { - $refs[$ref['processor_id']] = $ref['field_id']; - return $refs; - }, [] ); + $item_processor_id = Caldera_Forms::do_magic_tags( $item_processor_tag ); - $participant_ids = array_reduce( $order['line_items'], function( $ids, $item ) { - if ( $item['entity_table'] == 'civicrm_participant' ) { - $ids[] = $item['entity_id']; - } - return $ids; - }, [] ); + if ( strpos( $item_processor_id, 'civicrm_line_item' ) && empty( ( array ) $transient->line_items->$item_processor_id ) ) return $line_items; - $participant_items = array_reduce( $order['line_items'], function( $items, $item ) { - if ( $item['entity_table'] == 'civicrm_participant' ) { - $items[$item['entity_id']] = $item; - } - return $items; - }, [] ); + $line_item = $transient->line_items->$item_processor_id->params; - $participants = civicrm_api3( 'Participant', 'get', [ - 'id' => [ 'IN' => $participant_ids ], - 'options' => [ 'limit' => 0 ] - ] ); + if ( isset( $line_item['line_item'][0]['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) + $this->total_tax_amount += $line_item['line_item'][0]['tax_amount']; - $participants = array_reduce( $participants['values'], function( $participants, $participant ) { - $participants[] = $participant; - return $participants; - }, [] ); + // set membership as pending + if ( isset( $line_item['params']['membership_type_id'] ) && $this->is_pay_later ) { + $line_item['params']['status_id'] = 'Pending'; + $line_item['params']['is_override'] = 1; + } - $refs = array_merge( $price_field_refs, $price_field_option_refs ); + // set participant as pending + if ( isset( $line_item['params']['event_id'] ) && $this->is_pay_later ) + $line_item['params']['status_id'] = 'Pending from pay later'; - array_map( function( $processor_id, $field_id ) use ( $discounts_used, $transient, $order, $participants, $participant_items ) { + $line_item['processor_id'] = $item_processor_id; - $discount = isset( $discounts_used[$field_id] ) ? $discounts_used[$field_id] : false; + $line_items[] = $line_item; - if ( ! $discount ) return; + return $line_items; - $processor_id = $this->plugin->processors->processors['participant']->parse_processor_id( $processor_id ); + }, [] ); - $event_id = $transient->events->$processor_id->event_id; + } - $participant = array_filter( $participants, function( $participant ) use ( $event_id ) { - return $participant['event_id'] == $event_id; - } ); + /** + * Reformats the line items to correctly add otpions like donations + * assigned to the right enity, participant in this case. + * + * @since 1.0.1 + * @param array $line_items The formated line items + * @param array $config The processor config + * @param array $form The form config + * @return array $line_items The reformatted line items + */ + public function maybe_format_line_items_to_entity( $line_items, $config, $form ) { - $participant = array_pop( $participant ); + $participant_processors = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + $membership_pprocessors = $this->plugin->helper->get_processor_by_type( 'civicrm_membership', $form ); + $processors = []; - if ( ! $participant ) return; + if ( is_array( $participant_processors ) ) + $processors = array_merge( $processors, $participant_processors ); - try { - $discount_track = civicrm_api3( 'DiscountTrack', 'create', [ - 'item_id' => $discount['id'], - 'contact_id' => $order['contact_id'], - 'contribution_id' => $order['id'], - 'entity_table' => $participant_items[$participant['id']]['entity_table'], - 'entity_id' => $participant['id'], - 'description' => [ $participant_items[$participant['id']]['label'] ] - ] ); - } catch ( CiviCRM_API3_Exception $e ) { - Civi::log()->debug( 'Unable to track discount ' . $discount['code'] . ' for contribution id ' . $order['id'] ); - } + if ( is_array( $membership_pprocessors ) ) + $processors = array_merge( $processors, $membership_pprocessors ); - }, array_keys( $refs ), $refs ); - } + if ( empty( $processors ) ) return $line_items; - /** - * Runs when Order processor is post_processed if an order has been created. - * - * @since 1.0 - * @param array $order The created order result - * @param array $config The processor config - * @param array $form The form config - * @param string $processid The process id - */ - do_action( 'cfc_order_post_processor', $order, $config, $form, $processid ); + $formatted_items = []; + + array_map( function( $item ) use ( &$formatted_items, $processors, $form ) { + + if ( ! isset( $item['processor_entity'] ) || empty( $item['processor_entity'] ) ) { + + $formatted_items[$item['processor_id']] = $item; + + } else { + + $item['line_item'] = array_map( function( $line ) use ( $item, $processors ) { + $line['entity_table'] = $processors[$item['processor_entity']]['type']; + return $line; + }, $item['line_item'] ); + + if ( isset( $formatted_items[$item['processor_entity']]['line_item'] ) ) { + + $formatted_items[$item['processor_entity']]['line_item'] = array_reduce( $item['line_item'], function( $lines, $line ) { + $lines[] = $line; + return $lines; + }, $formatted_items[$item['processor_entity']]['line_item'] ); + + } else { + + $formatted_items[$item['processor_entity']]['line_item'] = $item['line_item']; + + } + + if ( ! isset( $formatted_items[$item['processor_entity']]['params'] ) && isset( $item['params'] ) ) + $formatted_items[$item['processor_entity']]['params'] = $item['params']; + + // recalculate all item selections for this line_item and update fee amount + if ( isset( $formatted_items[$item['processor_entity']]['params'] ) ) { + + $fees = array_column( $formatted_items[$item['processor_entity']]['line_item'], 'line_total' ); + + $taxes = array_column( $formatted_items[$item['processor_entity']]['line_item'], 'tax_amount' ); + + $formatted_items[$item['processor_entity']]['params']['fee_amount'] = ! empty( $taxes ) + ? array_sum( array_merge( $fees, $taxes ) ) + : array_sum( $fees ); + + } + + } - } + }, $line_items ); + return $formatted_items; } @@ -407,11 +419,9 @@ public function post_processor( $config, $form, $processid ) { * previous membership of the same type. * * @since 0.4.4 - * @param array $config Processor configuration * @param array $form Form configuration - * @param string $processid The process id */ - function preserve_membership_join_date( $config, $form, $processid ) { + function preserve_membership_join_date( $form ) { $transient = $this->plugin->transient->get(); @@ -476,7 +486,6 @@ function preserve_membership_join_date( $config, $form, $processid ) { } } - return $form; } /** @@ -581,6 +590,146 @@ public function add_payment_processor_hooks( $form, $referrer, $process_id ) { } } + /** + * Create premium. + * + * @since 1.0 + * @param array $order The order/contribution + * @param array $form_values The submitted form values + * @param array $config The processor config + */ + public function create_premium( $order, $form_values, $config ) { + + global $transdata; + + if ( ! isset( $form_values['product_id'] ) ) return; + + if ( ! $order ) return; + + $params = [ + 'product_id' => $form_values['product_id'], + 'contribution_id' => $order['id'], + 'quantity' => 1 // FIXME, can this be set via UI? + ]; + + if ( isset( $transdata['data'][$config['product_id'] . '_option'] ) ) + $params['product_option'] = $transdata['data'][$config['product_id'] . '_option']; + + try { + $premium = civicrm_api3( 'ContributionProduct', 'create', $params ); + } catch ( CiviCRM_API3_Exception $e ) { + // log error + } + } + + /** + * Track CiviDiscounts. + * + * @since 1.0 + * @param array $order The order with it's line items + */ + public function track_cividiscounts( $order ) { + + if ( ! $order || ! isset( $order['id'] ) ) return; + + if ( ! isset( $this->plugin->cividiscount ) ) return; + + if ( empty( $this->plugin->processors->processors['participant']->discounts_used ) ) return; + + if ( empty( $this->plugin->processors->processors['participant']->price_field_refs ) || empty( $this->plugin->processors->processors['participant']->price_field_option_refs ) ) return; + + $price_field_refs = $this->plugin->processors->processors['participant']->price_field_refs; + $price_field_option_refs = $this->plugin->processors->processors['participant']->price_field_option_refs; + $discounts_used = $this->plugin->processors->processors['participant']->discounts_used; + + $price_field_option_refs = array_reduce( $price_field_option_refs, function( $refs, $ref ) { + $refs[$ref['processor_id']] = $ref['field_id']; + return $refs; + }, [] ); + + $participant_ids = array_reduce( $order['line_items'], function( $ids, $item ) { + if ( $item['entity_table'] == 'civicrm_participant' ) + $ids[] = $item['entity_id']; + + return $ids; + }, [] ); + + $participant_items = array_reduce( $order['line_items'], function( $items, $item ) { + if ( $item['entity_table'] == 'civicrm_participant' ) + $items[$item['entity_id']] = $item; + + return $items; + }, [] ); + + $participants = civicrm_api3( 'Participant', 'get', [ + 'id' => [ 'IN' => $participant_ids ], + 'options' => [ 'limit' => 0 ] + ] ); + + if ( $participants['is_error'] && ! $participants['count'] ) return; + + $participants = array_reduce( $participants['values'], function( $participants, $participant ) { + $participants[] = $participant; + return $participants; + }, [] ); + + $refs = array_merge( $price_field_refs, $price_field_option_refs ); + + $transient = $this->plugin->transient->get(); + + array_map( function( $processor_id, $field_id ) use ( $discounts_used, $transient, $order, $participants, $participant_items ) { + + $discount = isset( $discounts_used[$field_id] ) ? $discounts_used[$field_id] : false; + + if ( ! $discount ) return; + + $processor_id = $this->plugin->processors->processors['participant']->parse_processor_id( $processor_id ); + + $event_id = $transient->events->$processor_id->event_id; + + $participant = array_filter( $participants, function( $participant ) use ( $event_id ) { + return $participant['event_id'] == $event_id; + } ); + + $participant = array_pop( $participant ); + + if ( ! $participant ) return; + + try { + $discount_track = civicrm_api3( 'DiscountTrack', 'create', [ + 'item_id' => $discount['id'], + 'contact_id' => $order['contact_id'], + 'contribution_id' => $order['id'], + 'entity_table' => $participant_items[$participant['id']]['entity_table'], + 'entity_id' => $participant['id'], + 'description' => [ $participant_items[$participant['id']]['label'] ] + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + Civi::log()->debug( 'Unable to track discount ' . $discount['code'] . ' for contribution id ' . $order['id'] ); + } + + }, array_keys( $refs ), $refs ); + + } + + /** + * Order has participants. + * + * @since 1.0.1 + * @param array $form_values The submitted values + * @return bool $has_participant + */ + public function has_participant_item( $line_items ) { + + if ( ! is_array( $line_items ) || empty( $line_items ) ) return false; + + $participant_line_items = array_filter( $line_items, function( $item ) { + return $item['line_item'][0]['entity_table'] === 'civicrm_participant'; + } ); + + return ! empty( $participant_line_items ); + } + /** * Send email confirmation/receipt. * diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index 69650b3..79bf5e8 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -284,7 +284,7 @@ public function build_price_field_refs( $form ) { return array_reduce( $line_items, function( $refs, $line_item ) use ( $form, $rendered_fields ) { - if ( ! empty( $line_item['config']['entity_table'] ) ) { + if ( ! empty( $line_item['config']['entity_table'] ) && in_array( $line_item['config']['entity_table'], [ 'civicrm_participant', 'civicrm_membership' ] ) ) { $price_field_slug = $line_item['config']['price_field_value']; From fc56982d506b045987d75d34020ec7be5df11674 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 25 Jan 2019 13:55:09 +0000 Subject: [PATCH 53/68] expose campaign_id --- processors/order/class-order-processor.php | 2 ++ processors/order/order_config.php | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index fb7588f..af5f0cb 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -152,6 +152,8 @@ public function processor( $config, $form, $processid ) { $form_values['currency'] = $config['currency']; + if ( ! empty( $config['campaign_id'] ) ) $form_values['campaign_id'] = $config['campaign_id']; + // contribution page for reciepts if ( isset( $config['contribution_page_id'] ) ) $form_values['contribution_page_id'] = $config['contribution_page_id']; diff --git a/processors/order/order_config.php b/processors/order/order_config.php index b789033..5621d67 100644 --- a/processors/order/order_config.php +++ b/processors/order/order_config.php @@ -32,6 +32,12 @@ 'is_test' => 0, ] ); +$campaigns = civicrm_api3( 'Campaign', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'options' => [ 'limit' => 0 ], +] ); + ?>

Note: This processor does not process payment transactions on it\'s own, it just creates a Contribution in CiviCRM with single or multiple line items. In order to process live payment transaction, a Caldera Forms add-on is needed. Currently this processor intergrates with Caldera Forms\' Stripe and Authorize.net add-ons for single/one-off payments.', 'caldera-forms-civicrm' ) ); ?>

@@ -102,6 +108,19 @@
+ +
+ +
+ +
+
+
From a63964e1fdec860cff4974e4d7cc2e3098d056f4 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Fri, 25 Jan 2019 14:47:25 +0000 Subject: [PATCH 54/68] add description --- processors/line-item/line_item_config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/processors/line-item/line_item_config.php b/processors/line-item/line_item_config.php index f3838ed..21d77d6 100644 --- a/processors/line-item/line_item_config.php +++ b/processors/line-item/line_item_config.php @@ -37,6 +37,7 @@
+

When \'Entity Table\' is set to CiviCRM Contribution, set the Participant processor magic tag if this is a Contribution Line Item associated to a particular Participant, like for example a Donation.', 'caldera-forms-civicrm') );?>

From 723a444ed2edd0d02c1133c989e8e2e8df2d9a4b Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Tue, 29 Jan 2019 01:20:26 +0000 Subject: [PATCH 55/68] empty option (N/A) should have id 0, otherwise Address.create will throw a fatal --- fields/civicrm_state/field.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fields/civicrm_state/field.php b/fields/civicrm_state/field.php index c7aa559..db3a2bf 100644 --- a/fields/civicrm_state/field.php +++ b/fields/civicrm_state/field.php @@ -58,7 +58,7 @@ return option.dataset.crmCountryId == countryId; } ); - if ( ! options.length ) options = new Option( 'N/A', 1 ); + if ( ! options.length ) options = new Option( 'N/A', 0 ); stateField.html( options ); From 0e2e1872ed9f7a31a38b35ec61d6c3acc2101895 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 31 Jan 2019 10:21:05 +0000 Subject: [PATCH 56/68] prevent duplicate options for the same line_item --- processors/order/class-order-processor.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index af5f0cb..bbd9827 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -330,7 +330,8 @@ public function build_line_items_params( $transient, $config, $form ) { $line_item['processor_id'] = $item_processor_id; - $line_items[] = $line_item; + if ( isset( $line_item['line_item'] ) ) + $line_items[] = $line_item; return $line_items; @@ -364,7 +365,7 @@ public function maybe_format_line_items_to_entity( $line_items, $config, $form ) $formatted_items = []; - array_map( function( $item ) use ( &$formatted_items, $processors, $form ) { + array_map( function( $item ) use ( &$formatted_items, $processors ) { if ( ! isset( $item['processor_entity'] ) || empty( $item['processor_entity'] ) ) { @@ -380,8 +381,15 @@ public function maybe_format_line_items_to_entity( $line_items, $config, $form ) if ( isset( $formatted_items[$item['processor_entity']]['line_item'] ) ) { $formatted_items[$item['processor_entity']]['line_item'] = array_reduce( $item['line_item'], function( $lines, $line ) { - $lines[] = $line; + + $price_field_values_ids = array_column( $lines, 'price_field_value_id' ); + + // there cannot be duplicated price field options for the same item + if ( ! in_array( $line['price_field_value_id'], $price_field_values_ids ) ) + $lines[] = $line; + return $lines; + }, $formatted_items[$item['processor_entity']]['line_item'] ); } else { From 929254d272d95dc8083a8c7d45831929cd61d6fb Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 31 Jan 2019 10:29:16 +0000 Subject: [PATCH 57/68] support quantity for participant line items --- .../line-item/class-line-item-processor.php | 11 +++++++++-- processors/line-item/line_item_config.php | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index e08a9bb..d6c9c44 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -201,9 +201,16 @@ public function process_participant( $config, $form, $transient, $price_field_va // if price field is disabled by cfc we won't have a price_field_value // if ( ! $price_field_value['id'] ) return; - if ( isset( $config['is_other_amount'] ) ) { - $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + + if ( isset( $config['is_other_amount'] ) ) $price_field_value[0]['line_total'] = $price_field_value[0]['unit_price'] = $price_field_value[0]['amount'] = $form_values['amount']; + + // handle qty and head count + if ( isset( $form_values['qty'] ) && isset( $config['is_fixed_price_field'] ) && ! isset( $config['is_other_amount'] ) ) { + $price_field_value[0]['qty'] = $form_values['qty']; + $price_field_value[0]['count'] = $form_values['qty']; + $price_field_value[0]['line_total'] = $price_field_value[0]['line_total'] * $form_values['qty']; } // get price field diff --git a/processors/line-item/line_item_config.php b/processors/line-item/line_item_config.php index 21d77d6..f7d1dc4 100644 --- a/processors/line-item/line_item_config.php +++ b/processors/line-item/line_item_config.php @@ -72,6 +72,21 @@
+ +
+ +
+ +
+
+ + +
+
+ +
+
+
@@ -104,6 +119,10 @@ var is_fixed = $( this ).prop( 'checked' ); $( '.binded_price_field', $( price_field_value ) ).toggle( ! is_fixed ); $( '.fixed_price_field', $( price_field_value ) ).toggle( is_fixed ); + // qty section + var condition = $( entity_table + ' select' ).val() == 'civicrm_participant' && is_fixed; + $( '#' + prId + '_qty' ).toggle( condition ); + $( '#' + prId + '_use_qty_as_count' ).toggle( condition ); } ).trigger( 'change' ); $( is_other_amount + ' input' ).on( 'change', function( i, el ) { From 245ef37d94009f5acc4da020d4c38f6eec7ae21a Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 31 Jan 2019 17:58:24 +0000 Subject: [PATCH 58/68] respect event's allow_same_participant_emails setting and allow registration --- .../class-participant-processor.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index 79bf5e8..25815f2 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -157,7 +157,7 @@ public function pre_processor( $config, $form, $processid ) { $is_registered = is_array( $this->registrations[$config['processor_id']] ); // store data in transient if is not registered - if ( ! $is_registered ) { + if ( ! $is_registered || $this->is_registered_and_same_email_allowed( $is_registered, $event ) ) { $transient->participants->{$config['processor_id']}->params = $form_values; $this->plugin->transient->save( $transient->ID, $transient ); @@ -188,7 +188,7 @@ public function pre_processor( $config, $form, $processid ) { } } - if ( ! $config['is_monetary'] && ! $is_registered ) { + if ( ( ! $config['is_monetary'] && ! $is_registered ) || ( ! $config['is_monetary'] && $this->is_registered_and_same_email_allowed( $is_registered, $event ) ) ) { try { $create_participant = civicrm_api3( 'Participant', 'create', $form_values ); if ( ! $create_participant['is_error'] && $config['is_email_receipt'] ) { @@ -879,6 +879,8 @@ public function get_notice( $processor_id, $form, $add_filter = false ) { $event = $this->events[$processor_id]; $participant = $this->registrations[$processor_id]; + if ( isset( $event['allow_same_participant_emails'] ) && $event['allow_same_participant_emails'] ) return; + // notices filter $filter = 'cfc_notices_to_render'; // cfc_notices_to_render filter callback @@ -1178,4 +1180,16 @@ public function parse_processor_id( $processor_id ) { return strpos( $processor_id, '#' ) ? substr( $processor_id, 0, strpos( $processor_id, '#' ) ) : $processor_id; } + /** + * Checkes whether a participant is registered and same email address is allowed. + * + * @since 1.0 + * @param bool $is_registered Participant is registered + * @param array $event The event settings + * @return bool $is_allowed + */ + public function is_registered_and_same_email_allowed( $is_registered, $event ) { + return $is_registered && isset( $event['allow_same_participant_emails'] ) && $event['allow_same_participant_emails']; + } + } From e3c726342b3c49ca121c3ecacba689d6de2d022b Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 31 Jan 2019 18:22:35 +0000 Subject: [PATCH 59/68] only override entity_table for participants being processed --- processors/order/class-order-processor.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index bbd9827..7de5467 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -355,6 +355,8 @@ public function maybe_format_line_items_to_entity( $line_items, $config, $form ) $membership_pprocessors = $this->plugin->helper->get_processor_by_type( 'civicrm_membership', $form ); $processors = []; + $transient = $this->plugin->transient->get(); + if ( is_array( $participant_processors ) ) $processors = array_merge( $processors, $participant_processors ); @@ -365,7 +367,7 @@ public function maybe_format_line_items_to_entity( $line_items, $config, $form ) $formatted_items = []; - array_map( function( $item ) use ( &$formatted_items, $processors ) { + array_map( function( $item ) use ( &$formatted_items, $processors, $transient ) { if ( ! isset( $item['processor_entity'] ) || empty( $item['processor_entity'] ) ) { @@ -373,9 +375,13 @@ public function maybe_format_line_items_to_entity( $line_items, $config, $form ) } else { - $item['line_item'] = array_map( function( $line ) use ( $item, $processors ) { - $line['entity_table'] = $processors[$item['processor_entity']]['type']; + $item['line_item'] = array_map( function( $line ) use ( $item, $processors, $transient ) { + // only override entity_table for participants being processed + if ( ! empty( ( array ) $transient->participants->{$item['processor_entity']} ) ) + $line['entity_table'] = $processors[$item['processor_entity']]['type']; + return $line; + }, $item['line_item'] ); if ( isset( $formatted_items[$item['processor_entity']]['line_item'] ) ) { From 191c06a8439dcec680cd9118b5dd4ab575b16af8 Mon Sep 17 00:00:00 2001 From: Andrei Mondoc Date: Thu, 31 Jan 2019 19:34:46 +0000 Subject: [PATCH 60/68] prevent undefined index/variable notices --- fields/civicrm_state/field.php | 2 +- includes/class-civicrm-caldera-forms-helper.php | 15 ++++++++++----- processors/order/class-order-processor.php | 6 +++--- .../participant/class-participant-processor.php | 6 ++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/fields/civicrm_state/field.php b/fields/civicrm_state/field.php index db3a2bf..a01ea78 100644 --- a/fields/civicrm_state/field.php +++ b/fields/civicrm_state/field.php @@ -32,7 +32,7 @@