/var/www/html_uk/wp-content/plugins/automatewoo/includes/Triggers/Customer_Win_Back.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<?php

namespace AutomateWoo;

use 
AutomateWoo\Exceptions\InvalidArgument;
use 
AutomateWoo\Triggers\AbstractBatchedDailyTrigger;
use 
RuntimeException;

defined'ABSPATH' ) || exit;

/**
 * @class Trigger_Customer_Win_Back
 */
class Trigger_Customer_Win_Back extends AbstractBatchedDailyTrigger {

    
/**
     * @var string[]
     */
    
public $supplied_data_items = [ 'customer''order' ];

    
/**
     * Load admin details.
     */
    
public function load_admin_details() {
        
$this->title        __'Customer Win Back''automatewoo' );
        
$this->description  __"This trigger fires for customers based on the date of their last paid order. The 'order based' variables, rules and actions used by this trigger only refer to the customer's last paid order."'automatewoo' );
        
$this->description .= ' ' $this->get_description_text_workflow_not_immediate();
        
$this->group        __'Customers''automatewoo' );
    }

    
/**
     * Load fields.
     */
    
public function load_fields() {
        
$period = ( new Fields\Positive_Number() )
            ->
set_name'days_since_last_purchase' )
            ->
set_title__'Minimum days since purchase''automatewoo' ) )
            ->
set_description__"Defines the minimum number of days to wait after a customer's last purchase."'automatewoo' ) )
            ->
set_required();

        
$period_max = ( new Fields\Number() )
            ->
set_name'days_since_last_purchase_max' )
            ->
set_title__'Maximum days since purchase''automatewoo' ) )
            ->
set_description__"Defines the maximum number of days after the customer's last purchase that this trigger will fire. The default value will be 3 greater than the value of the minimum days field."'automatewoo' ) );

        
$repeat = ( new Fields\Checkbox() )
            ->
set_name'enable_repeats' )
            ->
set_title__'Enable repeats''automatewoo' ) )
            ->
set_description__'If checked this trigger will repeatedly fire after the minimum last purchase date passes and the customer has not made a purchase. E.g. if the minimum is set to 30 days the trigger will fire 30 days after the customers last purchase and every 30 days from then until the maximum is reached or the customer makes another purchase. If unchecked the trigger will not repeat until the customer makes a new purchase.''automatewoo' ) );

        
$this->add_field$period );
        
$this->add_field$period_max );
        
$this->add_field$this->get_field_time_of_day() );
        
$this->add_field$repeat );
    }

    
/**
     * Get a batch of items to process for given workflow.
     *
     * @param Workflow $workflow
     * @param int      $offset The batch query offset.
     * @param int      $limit  The max items for the query.
     *
     * @return array[] Array of items in array format. Items will be stored in the database so they should be IDs not objects.
     */
    
public function get_batch_for_workflowWorkflow $workflowint $offsetint $limit ): array {
        
$tasks = [];

        foreach ( 
$this->get_customers_matching_last_purchase_range$workflow$limit$offset ) as $customer ) {
            
$tasks[] = [
                
'customer' => $customer->get_id(),
            ];
        }

        return 
$tasks;
    }

    
/**
     * Process a single item for a workflow to process.
     *
     * @param Workflow $workflow
     * @param array    $item
     *
     * @throws InvalidArgument If customer is not set.
     * @throws RuntimeException If there is an error.
     */
    
public function process_item_for_workflowWorkflow $workflow, array $item ) {
        if ( ! isset( 
$item['customer'] ) ) {
            throw 
InvalidArgument::missing_required'customer' );
        }

        
$customer Customer_Factory::get$item['customer'] );
        if ( ! 
$customer ) {
            throw new 
RuntimeException'Customer was not found.' );
        }

        
$last_paid_order $customer->get_nth_last_paid_order);
        if ( ! 
$last_paid_order ) {
            throw new 
RuntimeException'The customer has no paid orders. An order may have been edited or deleted since starting the job.' );
        }

        
$workflow->maybe_run(
            [
                
'customer' => $customer,
                
'order'    => $last_paid_order,
            ]
        );
    }

    
/**
     * Fetch users by date using the last order meta field.
     *
     * @param Workflow $workflow
     * @param int      $limit
     * @param int      $offset
     *
     * @return Customer[]
     */
    
protected function get_customers_matching_last_purchase_range$workflow$limit$offset ) {
        
$min_date $this->get_min_last_order_date$workflow );
        
$max_date $this->get_max_last_order_date$workflow );

        if ( ! 
$min_date ) {
            
// a minimum date must be set
            
return [];
        }

        
$query = new Customer_Query();
        
$query->set_limit$limit );
        
$query->set_offset$offset );
        
$query->where'last_purchased'$min_date'<' );
        
$query->set_ordering'last_purchased' );

        if ( 
$max_date ) {
            
$query->where'last_purchased'$max_date'>' );
        }

        return 
$query->get_results();
    }


    
/**
     * @param Workflow $workflow
     *
     * @return DateTime|bool
     */
    
protected function get_min_last_order_date$workflow ) {
        
$days $workflow->get_trigger_option'days_since_last_purchase' );

        if ( ! 
$days ) {
            return 
false;
        }

        
$days = -* (int) $days;
        
$date = new DateTime();
        
$date->modify"{$days} days" );

        return 
$date;
    }


    
/**
     * @param Workflow $workflow
     *
     * @return DateTime|bool
     */
    
protected function get_max_last_order_date$workflow ) {
        
$days $workflow->get_trigger_option'days_since_last_purchase_max' );

        if ( ! 
$days ) {
            
// default to 3 greater than the minimum days field
            
$days absint$workflow->get_trigger_option'days_since_last_purchase' ) ) + 3;
        }

        
$days = -* (int) $days;
        
$date = new DateTime();
        
$date->modify"{$days} days" );

        return 
$date;
    }

    
/**
     * @param Workflow $workflow
     *
     * @return bool
     */
    
public function validate_workflow$workflow ) {
        
$customer          $workflow->data_layer()->get_customer();
        
$most_recent_order $workflow->data_layer()->get_order();
        
$enable_repeats    $workflow->get_trigger_option'enable_repeats' );

        if ( ! 
$customer || ! $most_recent_order ) {
            return 
false;
        }

        
// exclude customers with active subscriptions
        // these customers are still active but their last purchase date might suggest they are inactive
        // TODO in the future the end date of the customers last subscription should be factored in to this logic
        
if ( Integrations::is_subscriptions_active() && $customer->is_registered() ) {
            if ( 
wcs_user_has_subscription$customer->get_user_id(), '''active' ) ) {
                return 
false;
            }
        }

        
// for accuracy, we use the actual order date instead of Customer::get_date_last_purchased()
        
$last_purchase_date  aw_normalize_date$most_recent_order->get_date_created() ); // convert to UTC
        
$min_last_order_date $this->get_min_last_order_date$workflow );

        if ( ! 
$min_last_order_date || ! $last_purchase_date ) {
            return 
false;
        }

        
// update the stored last purchase date
        
$customer->set_date_last_purchased$last_purchase_date );
        
$customer->save();

        
// check that the user has not made a purchase since the start of the delay period
        
if ( $last_purchase_date->getTimestamp() > $min_last_order_date->getTimestamp() ) {
            return 
false;
        }

        
// if repeats are enabled the wait period should start at the last time the workflow was run or queued
        // if repeats are disabled the date range should start at the last order date
        
$wait_period $enable_repeats $min_last_order_date $last_purchase_date;

        if ( 
$workflow->get_timing_type() !== 'immediately' ) {
            
// check workflow has not been added to the queue already

            
$query = new Queue_Query();
            
$query->where_workflow$workflow->get_translation_ids() );
            
$query->where_date_created$wait_period'>' );
            
$query->where_customer_or_legacy_user$customer );

            if ( 
$query->has_results() ) {
                return 
false;
            }
        }

        
// check the workflow has not run already
        
$log_query = new Log_Query();
        
$log_query->where_workflow$workflow->get_translation_ids() );
        
$log_query->where_date$wait_period'>' );
        
$log_query->where_customer_or_legacy_user$customer );

        if ( 
$log_query->has_results() ) {
            return 
false;
        }

        return 
true;
    }


    
/**
     * @param Workflow $workflow
     *
     * @return bool
     */
    
public function validate_before_queued_event$workflow ) {
        
$customer $workflow->data_layer()->get_customer();

        if ( ! 
$customer ) {
            return 
false;
        }

        
$min_last_order_date $this->get_min_last_order_date$workflow );
        
$last_purchase_date  $customer->get_date_last_purchased();

        if ( ! 
$min_last_order_date || ! $last_purchase_date ) {
            return 
false;
        }

        
// check that the user has not made a purchase while the workflow was queued
        
if ( $last_purchase_date->getTimestamp() > $min_last_order_date->getTimestamp() ) {
            return 
false;
        }

        return 
true;
    }
}