Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 298
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 4
zeroBSCRM_InvoicesMetaboxSetup
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__InvoicePro
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_Invoice
0.00% covered (danger)
0.00%
0 / 159
0.00% covered (danger)
0.00%
0 / 5
3422
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
132
 save_data
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 1
1722
 post_save_data
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 updateMessage
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_InvoiceFiles
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 3
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
56
 save_data
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
182
zeroBS__Metabox_InvoiceTags
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_InvoiceActions
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 2
42
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 * V3.0
6 *
7 * Copyright 2020 Automattic
8 *
9 * Date: 20/02/2019
10 */
11
12/*
13======================================================
14    Breaking Checks ( stops direct access )
15    ====================================================== */
16defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
17/*
18======================================================
19    / Breaking Checks
20    ====================================================== */
21
22/*
23======================================================
24    Init Func
25    ====================================================== */
26
27function zeroBSCRM_InvoicesMetaboxSetup() {
28
29    // main detail
30    $zeroBS__Metabox_Invoice = new zeroBS__Metabox_Invoice( __FILE__ );
31
32    // actions (status + save)
33    $zeroBS__Metabox_InvoiceActions = new zeroBS__Metabox_InvoiceActions( __FILE__ );
34
35    // invoice tags box
36    $zeroBS__Metabox_InvoiceTags = new zeroBS__Metabox_InvoiceTags( __FILE__ );
37
38    // external sources
39    $zeroBS__Metabox_ExtSource = new zeroBS__Metabox_ExtSource( __FILE__, 'invoice', 'zbs-add-edit-invoice-edit' );
40
41    // files
42    $zeroBS__Metabox_InvoiceFiles = new zeroBS__Metabox_InvoiceFiles( __FILE__ );
43}
44
45add_action( 'admin_init', 'zeroBSCRM_InvoicesMetaboxSetup' );
46
47/*
48======================================================
49    / Init Func
50    ====================================================== */
51
52/*
53======================================================
54    Invoicing Metabox
55    ====================================================== */
56
57class zeroBS__Metabox_Invoice extends zeroBS__Metabox {
58
59    // this is for catching 'new' invoice
60    private $newRecordNeedsRedir = false;
61
62    public function __construct( $plugin_file ) {
63
64        // set these
65        $this->objType         = 'invoice';
66        $this->metaboxID       = 'zerobs-invoice-edit';
67        $this->metaboxTitle    = __( 'Invoice Information', 'zero-bs-crm' ); // will be headless anyhow
68        $this->headless        = true;
69        $this->metaboxScreen   = 'zbs-add-edit-invoice-edit';
70        $this->metaboxArea     = 'normal';
71        $this->metaboxLocation = 'high';
72        $this->saveOrder       = 1;
73        $this->capabilities    = array(
74
75            'can_hide'        => false,             // can be hidden
76            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
77            'can_accept_tabs' => true,              // can/can't accept tabs onto it
78            'can_become_tab'  => false,             // can be added as tab
79            'can_minimise'    => true,              // can be minimised
80            'can_move'        => true,              // can be moved
81
82        );
83
84        // call this
85        $this->initMetabox();
86    }
87
88    public function html( $invoice, $metabox ) {
89
90        // localise ID
91        $invoiceID = is_array( $invoice ) && isset( $invoice['id'] ) ? (int) $invoice['id'] : -1;
92
93        global $zbs;
94
95        // Prefill ID and OBJ are added to the #zbs_invoice to aid in prefilling the data (when drawn with JS)
96        $prefill_id    = -1;
97        $prefill_obj   = -1;
98        $prefill_email = '';
99        $prefill_name  = '';
100
101        if ( ! empty( $_GET['zbsprefillcust'] ) ) {
102            $prefill_id    = (int) $_GET['zbsprefillcust'];
103            $prefill_obj   = ZBS_TYPE_CONTACT;
104            $prefill_email = zeroBS_customerEmail( $prefill_id );
105            $prefill_name  = $zbs->DAL->contacts->getContactNameWithFallback( $prefill_id );
106        }
107        if ( ! empty( $_GET['zbsprefillco'] ) ) {
108            $prefill_id    = (int) $_GET['zbsprefillco'];
109            $prefill_obj   = ZBS_TYPE_COMPANY;
110            $prefill_email = zeroBS_companyEmail( $prefill_id );
111            $prefill_name  = $zbs->DAL->companies->getCompanyNameEtc( $prefill_id );
112        }
113        ?>
114        <?php /* AJAX NONCE */ ?>
115        <script type="text/javascript">var zbscrmjs_secToken = '<?php echo esc_js( wp_create_nonce( 'zbscrmjs-ajax-nonce' ) ); ?>';</script>
116        <?php /* END OF NONCE */ ?>
117        <?php
118        // AJAX NONCE for inv sending... defunct v3.0?
119        echo '<input type="hidden" name="inv-ajax-nonce" id="inv-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'inv-ajax-nonce' ) ) . '" />';
120
121        // invoice UI divs (loader and canvas)
122        echo '<div id="zbs_loader"><div class="ui active dimmer inverted"><div class="ui text loader">' . esc_html( __( 'Loading Invoice...', 'zero-bs-crm' ) ) . '</div></div><p></p></div>';
123        echo "<div id='zbs_invoice' class='zbs_invoice_html_canvas' data-invid='" . esc_attr( $invoiceID ) . "'></div>";
124
125        // we pass the hash along the chain here too :)
126        if ( isset( $invoice['hash'] ) ) {
127            echo '<input type="hidden" name="zbsi_hash" id="zbsi_hash" value="' . esc_attr( $invoice['hash'] ) . '" />';
128        }
129
130        // custom fields
131        $customFields = $zbs->DAL->getActiveCustomFields( array( 'objtypeid' => ZBS_TYPE_INVOICE ) );
132        if ( ! is_array( $customFields ) ) {
133            $customFields = array();
134        }
135
136        // pass data:
137        ?>
138        <script type="text/javascript">
139
140            <?php
141            if ( $prefill_obj > 0 ) {
142                echo 'var zbsJS_prefillobjtype = ' . esc_js( $prefill_obj ) . ';';
143            }
144            if ( $prefill_id > 0 ) {
145                echo 'var zbsJS_prefillid = ' . esc_js( $prefill_id ) . ';';
146            }
147            echo 'var zbsJS_prefillemail = \'' . esc_js( $prefill_email ) . '\';';
148            echo 'var zbsJS_prefillname = \'' . esc_js( $prefill_name ) . '\';';
149
150            // only sendemail if have active template :)
151            echo 'var zbsJS_invEmailActive = ' . ( zeroBSCRM_get_email_status( ZBSEMAIL_EMAILINVOICE ) == 1 ? '1' : '-1' ) . ';';
152            ?>
153        </script>
154        <div id="zbs-invoice-custom-fields-holder" style="display:none">
155            <table>
156                <?php
157
158                // here we put the fields out then:
159                // 1) copy the fields into the UI
160                foreach ( $customFields as $cfK => $cF ) {
161
162                    zeroBSCRM_html_editField_for_invoices( $invoice, $cfK, $cF, 'zbsi_' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
163
164                }
165                ?>
166            </table>
167        </div>
168        <?php
169
170        // allow hook-ins from invoicing pro etc.
171        do_action( 'zbs_invoicing_append' );
172    }
173
174    public function save_data( $invoiceID, $invoice ) {
175
176        if ( ! defined( 'ZBS_OBJ_SAVED' ) ) {
177
178            define( 'ZBS_OBJ_SAVED', 1 );
179
180            global $zbs;
181
182            // check this
183            if ( empty( $invoiceID ) || $invoiceID < 1 ) {
184                $invoiceID = -1;
185            }
186
187            // retrieve existing
188            $existing_invoice = $zbs->DAL->invoices->getInvoice( $invoiceID );
189
190            $autoGenAutonumbers = true; // generate if not set :)
191            $removeEmpties      = false; // req for autoGenAutonumbers
192            $invoice            = zeroBS_buildObjArr( $_POST, array(), 'zbsi_', '', $removeEmpties, ZBS_TYPE_INVOICE, $autoGenAutonumbers );
193
194            // Use the tag-class function to retrieve any tags so we can add inline.
195            // Save tags against objid
196            $invoice['tags'] = zeroBSCRM_tags_retrieveFromPostBag( true, ZBS_TYPE_INVOICE );
197
198            // pay via
199            $invoice['pay_via'] = isset( $_POST['pay_via'] ) && (int) $_POST['pay_via'] === -1 ? -1 : 0;
200
201            // new way.. now not limited to 30 lines as now they are stored in [] type array in JS draw
202            $zbsInvoiceLines = array();
203
204            // gather lineitem data from POST
205            // really this could use a refactor on the JS side so the POST data is structured how we want
206            foreach ( $_POST['zbsli_itemname'] as $k => $v ) {
207
208                $ks = sanitize_text_field( $k ); // at least this
209
210                if ( ! isset( $zbsInvoiceLines[ $ks ]['net'] ) ) {
211                    $zbsInvoiceLines[ $ks ]['net'] = 0.0;
212                }
213                $zbsInvoiceLines[ $ks ]['title']    = sanitize_text_field( $_POST['zbsli_itemname'][ $k ] );
214                $zbsInvoiceLines[ $ks ]['desc']     = sanitize_textarea_field( $_POST['zbsli_itemdes'][ $k ] );
215                $zbsInvoiceLines[ $ks ]['quantity'] = sanitize_text_field( $_POST['zbsli_quan'][ $k ] );
216                $zbsInvoiceLines[ $ks ]['price']    = sanitize_text_field( $_POST['zbsli_price'][ $k ] );
217
218                // calc a net, if have elements
219                if ( ! empty( $zbsInvoiceLines[ $ks ]['quantity'] ) && ! empty( $zbsInvoiceLines[ $ks ]['price'] ) ) {
220
221                    $zbsInvoiceLines[ $ks ]['net'] = $zbsInvoiceLines[ $ks ]['quantity'] * $zbsInvoiceLines[ $ks ]['price'];
222
223                } else {
224
225                    // leave net as empty :)
226
227                }
228
229                // taxes now stored as csv in 'taxes', 'tax' contains a total, but that's not passed by MS UI (yet? not needed?)
230                $zbsInvoiceLines[ $ks ]['tax']   = 0;
231                $zbsInvoiceLines[ $ks ]['taxes'] = empty( $_POST['zbsli_tax'][ $k ] ) ? '' : sanitize_text_field( $_POST['zbsli_tax'][ $k ] );
232
233                /*
234                as at 22/2/19, each lineitem here could hold:
235                    'order' => '',
236                    'title' => '',
237                    'desc' => '',
238                    'quantity' => '',
239                    'price' => '',
240                    'currency' => '',
241                    'net' => '',
242                    'discount' => '',
243                    'fee' => '',
244                    'shipping' => '',
245                    'shipping_taxes' => '',
246                    'shipping_tax' => '',
247                    'taxes' => '',
248                    'tax' => '',
249                    'total' => '',
250                    'created' => '',
251                    'lastupdated' => '',
252                */
253            }
254
255            if ( count( $zbsInvoiceLines ) > 0 ) {
256                $invoice['lineitems'] = $zbsInvoiceLines;
257            }
258
259            // other items to update
260
261            // hours or quantity switch
262            if ( ! empty( $_POST['invoice-customiser-type'] ) ) {
263                $invoice['hours_or_quantity'] = $_POST['invoice-customiser-type'] === 'hours' ? 0 : 1;
264            }
265
266            // totals passed
267            $invoice['discount']      = empty( $_POST['invoice_discount_total'] ) ? 0 : (float) sanitize_text_field( $_POST['invoice_discount_total'] );
268            $invoice['discount_type'] = empty( $_POST['invoice_discount_type'] ) ? 0 : sanitize_text_field( $_POST['invoice_discount_type'] );
269            $invoice['shipping']      = empty( $_POST['invoice_postage_total'] ) ? 0 : (float) sanitize_text_field( $_POST['invoice_postage_total'] );
270            // phpcs:ignore WordPress.Security.NonceVerification.Missing
271            $invoice['shipping_taxes'] = empty( $_POST['zbsli_tax_ship'] ) ? 0 : (float) sanitize_text_field( wp_unslash( $_POST['zbsli_tax_ship'] ) );
272            // or shipping_taxes (not set by MS script)
273
274            // ... js pass through :o Will be overwritten on php calc on addUpdate, actually, v3.0+
275            $invoice['total'] = empty( $_POST['zbs-inv-grand-total-store'] ) ? 0 : (float) sanitize_text_field( $_POST['zbs-inv-grand-total-store'] );
276
277            // assignments
278            $zbsInvoiceContact    = (int) $_POST['zbs_invoice_contact'];
279            $invoice['contacts']  = $zbsInvoiceContact > 0 ? array( $zbsInvoiceContact ) : array();
280            $zbsInvoiceCompany    = (int) $_POST['zbs_invoice_company'];
281            $invoice['companies'] = $zbsInvoiceCompany > 0 ? array( $zbsInvoiceCompany ) : array();
282            // Later use: 'address_to_objtype'
283
284            // other fields
285            if ( isset( $_POST['invoice_status'] ) ) {
286                $invoice['status'] = sanitize_text_field( $_POST['invoice_status'] );
287            }
288            if ( isset( $_POST['zbsi_logo'] ) ) {
289                $invoice['logo_url'] = sanitize_url( $_POST['zbsi_logo'] );
290            }
291
292            $ref_type = $zbs->settings->get( 'reftype' );
293            if ( $invoiceID === -1 && $ref_type === 'autonumber' ) {
294                $next_number            = $zbs->settings->get( 'refnextnum' );
295                $prefix                 = $zbs->settings->get( 'refprefix' );
296                $suffix                 = $zbs->settings->get( 'refsuffix' );
297                $invoice['id_override'] = $prefix . $next_number . $suffix;
298                ++$next_number;
299                $zbs->settings->update( 'refnextnum', $next_number );
300            } elseif ( isset( $_POST['zbsi_ref'] ) ) {
301                $invoice['id_override'] = sanitize_text_field( $_POST['zbsi_ref'] );
302            } else {
303
304                // ref should exist
305                if ( empty( $invoice['id_override'] ) && isset( $existing_invoice['id_override'] ) ) {
306
307                    // override empty with existing
308                    $invoice['id_override'] = $existing_invoice['id_override'];
309
310                }
311            }
312
313            // this needs to be translated to UTS (GMT)
314            if ( isset( $_POST['zbsi_date'] ) ) {
315                $invoice['date'] = sanitize_text_field( $_POST['zbsi_date'] );
316                $invoice['date'] = jpcrm_date_str_to_uts( $invoice['date'] );
317            }
318
319            // due date is now calculated on save, then stored as UTS, if passed this way:
320            // ... if due_date not set, editor will keep showing "due in x days" select
321            // ... once set, that'll always show as a datepicker, based on the UTS in due_date
322            if ( isset( $_POST['zbsi_due'] ) ) {
323
324                // days (-1 - 90)
325                $dueInDays = sanitize_text_field( $_POST['zbsi_due'] );
326
327                // got date + due days?
328                if ( isset( $invoice['date'] ) && $dueInDays >= 0 ) {
329
330                    // project it forward
331                    $invoice['due_date'] = $invoice['date'] + ( $dueInDays * 60 * 60 * 24 );
332
333                }
334            }
335            // ... this then catches datepicker-picked dates (if passed by datepicker variant to due_days)
336            if ( isset( $_POST['zbsi_due_date'] ) ) {
337                $invoice['due_date'] = sanitize_text_field( $_POST['zbsi_due_date'] );
338                $invoice['due_date'] = jpcrm_date_str_to_uts( $invoice['due_date'] );
339            }
340
341            // Custom Fields.
342            /*
343            $customFields = $zbs->DAL->getActiveCustomFields(array('objtypeid' => ZBS_TYPE_INVOICE));
344            if (!is_array($customFields)) $customFields = array();
345            foreach ($customFields as $cfK => $cfV){
346
347                if (isset($_POST['zbsi_'.$cfK]))    $invoice[$cfK]      = sanitize_text_field($_POST['zbsi_'.$cfK]);
348
349            }*/
350
351            // add/update
352            $addUpdateReturn = $zbs->DAL->invoices->addUpdateInvoice(
353                array(
354                    'id'               => $invoiceID,
355                    'data'             => $invoice,
356                    'limitedFields'    => -1,
357
358                    // here we want PHP to calculate the total, tax etc. where we don't calc all the specifics in js
359                    'calculate_totals' => 1,
360                )
361            );
362
363            // Note: For NEW objs, we make sure a global is set here, that other update funcs can catch
364            // ... so it's essential this one runs first!
365            // this is managed in the metabox Class :)
366            if ( $invoiceID === -1 && ! empty( $addUpdateReturn ) && $addUpdateReturn != -1 ) {
367
368                $invoiceID = $addUpdateReturn;
369                global $zbsJustInsertedMetaboxID;
370                $zbsJustInsertedMetaboxID = $invoiceID;
371
372                // set this so it redirs
373                $this->newRecordNeedsRedir = true;
374            }
375
376            // success?
377            if ( $addUpdateReturn > 0 ) {
378
379                // Update Msg
380                // this adds an update message which'll go out ahead of any content
381                // This adds to metabox: $this->updateMessages['update'] = zeroBSCRM_UI2_messageHTML('info olive mini zbs-not-urgent',__('Contact Updated',"zero-bs-crm"),'','address book outline','contactUpdated');
382                // This adds to edit page
383                $this->updateMessage();
384
385                // catch any non-critical messages
386                $nonCriticalMessages = $zbs->DAL->getErrors( ZBS_TYPE_INVOICE );
387                if ( is_array( $nonCriticalMessages ) && count( $nonCriticalMessages ) > 0 ) {
388                    $this->dalNoticeMessage( $nonCriticalMessages );
389                }
390            } else {
391
392                // fail somehow
393                $failMessages = $zbs->DAL->getErrors( ZBS_TYPE_INVOICE );
394
395                // show msg (retrieved from DAL err stack)
396                if ( is_array( $failMessages ) && count( $failMessages ) > 0 ) {
397                    $this->dalErrorMessage( $failMessages );
398                } else {
399                    $this->dalErrorMessage( array( __( 'Insert/Update Failed with general error', 'zero-bs-crm' ) ) );
400                }
401
402                // pass the pre-fill:
403                global $zbsObjDataPrefill;
404                $zbsObjDataPrefill = $invoice;
405
406            }
407        }
408
409        return $invoice;
410    }
411
412    // This catches 'new' contacts + redirs to right url
413    public function post_save_data( $objID, $obj ) {
414
415        if ( $this->newRecordNeedsRedir ) {
416
417            global $zbsJustInsertedMetaboxID;
418            if ( ! empty( $zbsJustInsertedMetaboxID ) && $zbsJustInsertedMetaboxID > 0 ) {
419
420                // redir
421                wp_redirect( jpcrm_esc_link( 'edit', $zbsJustInsertedMetaboxID, $this->objType ) );
422                exit( 0 );
423
424            }
425        }
426    }
427
428    public function updateMessage() {
429
430        global $zbs;
431
432        // zbs-not-urgent means it'll auto hide after 1.5s
433        // genericified from DAL3.0
434        $msg = zeroBSCRM_UI2_messageHTML( 'info olive mini zbs-not-urgent', $zbs->DAL->typeStr( $zbs->DAL->objTypeKey( $this->objType ) ) . ' ' . __( 'Updated', 'zero-bs-crm' ), '', 'address book outline', 'contactUpdated' );
435
436        $zbs->pageMessages[] = $msg;
437    }
438}
439
440/*
441======================================================
442    / Invoicing Metabox
443    ====================================================== */
444
445/*
446======================================================
447    Invoice Files Metabox
448    ====================================================== */
449
450class zeroBS__Metabox_InvoiceFiles extends zeroBS__Metabox {
451
452    public function __construct( $plugin_file ) {
453
454        // DAL3 switched for objType $this->postType = 'zerobs_customer';
455        $this->objType         = 'invoice';
456        $this->metaboxID       = 'zerobs-invoice-files';
457        $this->metaboxTitle    = __( 'Associated Files', 'zero-bs-crm' );
458        $this->metaboxScreen   = 'zbs-add-edit-invoice-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
459        $this->metaboxArea     = 'normal';
460        $this->metaboxLocation = 'low';
461        $this->capabilities    = array(
462
463            'can_hide'        => true, // can be hidden
464            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
465            'can_accept_tabs' => true,  // can/can't accept tabs onto it
466            'can_become_tab'  => true, // can be added as tab
467            'can_minimise'    => true, // can be minimised
468
469        );
470
471        // call this
472        $this->initMetabox();
473    }
474
475    public function html( $invoice, $metabox ) {
476
477            global $zbs;
478
479            $html = '';
480
481            // localise ID
482            $invoiceID = -1;
483        if ( is_array( $invoice ) && isset( $invoice['id'] ) ) {
484            $invoiceID = (int) $invoice['id'];
485        }
486
487            #} retrieve
488            $zbsFiles = array();
489        if ( $invoiceID > 0 ) {
490            $zbsFiles = zeroBSCRM_files_getFiles( 'invoice', $invoiceID );
491        }
492
493        ?>
494                <table class="form-table wh-metatab wptbp" id="wptbpMetaBoxMainItemFiles">
495
496                <?php
497
498                    // WH only slightly updated this for DAL3 - could do with a cleanup run (contact file edit has more functionality)
499
500                    #} Any existing
501                if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
502                    ?>
503                            <tr class="wh-large"><th><label><?php printf( esc_html( _n( '%s associated file', '%s associated files', count( $zbsFiles ), 'text-domain' ) ), esc_html( number_format_i18n( count( $zbsFiles ) ) ) ); ?></label></th>
504                                    <td id="zbsFileWrapInvoices">
505                                <?php
506                                    $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
507
508                                        $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
509
510                                        echo '<div class="zbsFileLine" id="zbsFileLineInvoice' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a> (<span class="zbsDelFile" data-delurl="' . esc_attr( $zbsFile['url'] ) . '"><i class="fa fa-trash"></i></span>)</div>';
511                                        ++$fileLineIndx;
512
513                                    }
514                                    ?>
515                                    </td></tr>
516                                    <?php
517
518                }
519                ?>
520
521                    <?php
522                    #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
523
524                        $html .= '<input type="file" id="zbsobj_file_attachment" name="zbsobj_file_attachment" size="25" class="zbs-dc">';
525
526                    ?>
527                            <tr class="wh-large"><th><label><?php esc_html_e( 'Add File', 'zero-bs-crm' ); ?>:</label><br />(<?php esc_html_e( 'Optional', 'zero-bs-crm' ); ?>)<br /><?php esc_html_e( 'Accepted File Types', 'zero-bs-crm' ); ?>:<br /><?php echo esc_html( zeroBS_acceptableFileTypeListStr() ); ?></th>
528                                <td>
529                                <?php
530                                wp_nonce_field( plugin_basename( __FILE__ ), 'zbsobj_file_attachment_nonce' );
531                                echo $html;
532                                ?>
533                    </td></tr>
534            
535            </table>
536            <script type="text/javascript">
537
538                var zbsInvoicesCurrentlyDeleting = false;
539                var zbsMetaboxFilesLang = {
540                    'err': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Error', 'zero-bs-crm' ) ) ); ?>',
541                    'unabletodel' : '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Unable to delete this file', 'zero-bs-crm' ) ) ); ?>',
542                            'viewcontact' : '<?php echo esc_html__( 'View contact', 'zero-bs-crm' ); ?>',
543                            'viewcompany' : '<?php echo esc_html( __( 'View', 'zero-bs-crm' ) . ' ' . jpcrm_label_company() ); ?>',
544                }
545
546                jQuery(function(){
547
548                    jQuery('.zbsDelFile').on( 'click', function(){
549
550                        if (!window.zbsInvoicesCurrentlyDeleting){
551
552                            // blocking
553                            window.zbsInvoicesCurrentlyDeleting = true;
554
555                            var delUrl = jQuery(this).attr('data-delurl');
556                            var lineIDtoRemove = jQuery(this).closest('.zbsFileLine').attr('id');
557
558                            if (typeof delUrl != "undefined" && delUrl != ''){
559
560
561
562                                    // postbag!
563                                    var data = {
564                                    'action': 'delFile',
565                                    'zbsfType': 'invoices',
566                                    'zbsDel':  delUrl, // could be csv, never used though
567                                    'zbsCID': <?php echo esc_html( $invoiceID ); ?>,
568                                    'sec': window.zbscrmjs_secToken
569                                    };
570
571                                    // Send it Pat :D
572                                    jQuery.ajax({
573                                            type: "POST",
574                                            url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
575                                            "data": data,
576                                            dataType: 'json',
577                                            timeout: 20000,
578                                            success: function(response) {
579
580                                            // visually remove
581                                            jQuery('#' + lineIDtoRemove).remove();
582
583                                            // file deletion errors, show msg:
584                                            if (typeof response.errors != "undefined" && response.errors.length > 0){
585
586                                                jQuery.each(response.errors,function(ind,ele){
587
588                                                    jQuery('#zerobs-invoice-files-box').append('<div class="ui warning message" style="margin-top:10px;">' + ele + '</div>');
589
590                                                });
591                                                     
592
593                                            }
594
595                                            },
596                                            error: function(response){
597
598                                            jQuery('#zerobs-invoice-files-box').append('<div class="ui warning message" style="margin-top:10px;"><strong>' + window.zbsMetaboxFilesLang.err + ':</strong> ' + window.zbsMetaboxFilesLang.unabletodel + '</div>');
599
600                                            }
601
602                                        });
603
604                            }
605
606                            window.zbsInvoicesCurrentlyDeleting = false;
607
608                        } // / blocking
609
610                    });
611
612                });
613
614
615            </script>
616            <?php
617    }
618
619    public function save_data( $invoiceID, $invoice ) {
620
621        global $zbsobj_justUploadedObjFile;
622        $id = $invoiceID;
623
624        if ( ! empty( $_FILES['zbsobj_file_attachment']['name'] ) &&
625            ( ! isset( $zbsobj_justUploadedObjFile ) ||
626                ( isset( $zbsobj_justUploadedObjFile ) && $zbsobj_justUploadedObjFile != $_FILES['zbsobj_file_attachment']['name'] )
627            )
628            ) {
629
630                /* --- security verification --- */
631            if ( ! wp_verify_nonce( $_POST['zbsobj_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
632                return $id;
633            } // end if
634
635            if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
636                return $id;
637            } // end if
638
639            if ( ! zeroBSCRM_permsInvoices() ) {
640                return $id;
641            }
642                /* - end security verification - */
643
644                // Blocking repeat-upload bug
645                $zbsobj_justUploadedObjFile = $_FILES['zbsobj_file_attachment']['name'];
646
647                // verify file extension and mime type
648            if ( jpcrm_file_check_mime_extension( $_FILES['zbsobj_file_attachment'] ) ) {
649
650                $invoice_dir_info = jpcrm_storage_dir_info_for_invoices( $invoiceID );
651                $upload           = jpcrm_save_admin_upload_to_folder( 'zbsobj_file_attachment', $invoice_dir_info['files'] );
652
653                if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
654                    wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
655                } else {
656                        // w mod - adds to array :)
657                        $zbsFiles = zeroBSCRM_files_getFiles( 'invoice', $invoiceID );
658
659                    if ( is_array( $zbsFiles ) ) {
660
661                        // add it
662                        $zbsFiles[] = $upload;
663
664                    } else {
665
666                        // first
667                        $zbsFiles = array( $upload );
668
669                    }
670
671                        // update
672                        zeroBSCRM_files_updateFiles( 'invoice', $invoiceID, $zbsFiles );
673
674                        // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
675                        // not req invoicing: do_action('zbs_post_upload_contact',$upload);
676                }
677            } else {
678                wp_die( "The file type that you've uploaded is not an accepted file format." );
679            }
680        }
681
682        return $invoice;
683    }
684}
685
686/*
687======================================================
688    / Attach files to invoice metabox
689    ====================================================== */
690
691/*
692======================================================
693    Invoicing Metabox Helpers
694    ====================================================== */
695function zeroBS__InvoicePro() {
696
697        $upTitle  = __( 'Want more from invoicing?', 'zero-bs-crm' );
698        $upDesc   = __( 'Accept Payments Online with Invoicing Pro.', 'zero-bs-crm' );
699        $upButton = __( 'Buy Now', 'zero-bs-crm' );
700        $upTarget = 'https://jetpackcrm.com/product/invoicing-pro/';
701
702        echo zeroBSCRM_UI2_squareFeedbackUpsell( $upTitle, $upDesc, $upButton, $upTarget );
703}
704
705/*
706======================================================
707    / Invoicing Metabox Helpers
708    ====================================================== */
709
710/*
711======================================================
712    Create Tags Box
713    ====================================================== */
714
715class zeroBS__Metabox_InvoiceTags extends zeroBS__Metabox_Tags {
716
717    public function __construct( $plugin_file ) {
718
719        $this->objTypeID = ZBS_TYPE_INVOICE;
720        // DAL3 switched for objType $this->postType = 'zerobs_customer';
721        $this->objType         = 'invoice';
722        $this->metaboxID       = 'zerobs-invoice-tags';
723        $this->metaboxTitle    = __( 'Invoice Tags', 'zero-bs-crm' );
724        $this->metaboxScreen   = 'zbs-add-edit-invoice-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
725        $this->metaboxArea     = 'side';
726        $this->metaboxLocation = 'high';
727        $this->showSuggestions = true;
728        $this->capabilities    = array(
729
730            'can_hide'        => true, // can be hidden
731            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
732            'can_accept_tabs' => false,  // can/can't accept tabs onto it
733            'can_become_tab'  => false, // can be added as tab
734            'can_minimise'    => true, // can be minimised
735
736        );
737
738        // call this
739        $this->initMetabox();
740    }
741
742    // html + save dealt with by parent class :)
743}
744
745/*
746======================================================
747    / Create Tags Box
748    ====================================================== */
749
750/*
751======================================================
752    Invoice Actions Metabox
753    ====================================================== */
754
755class zeroBS__Metabox_InvoiceActions extends zeroBS__Metabox {
756
757    public function __construct( $plugin_file ) {
758
759        // set these
760        $this->objType         = 'invoice';
761        $this->metaboxID       = 'zerobs-invoice-actions';
762        $this->metaboxTitle    = __( 'Invoice Actions', 'zero-bs-crm' ); // will be headless anyhow
763        $this->headless        = true;
764        $this->metaboxScreen   = 'zbs-add-edit-invoice-edit';
765        $this->metaboxArea     = 'side';
766        $this->metaboxLocation = 'high';
767        $this->saveOrder       = 1;
768        $this->capabilities    = array(
769
770            'can_hide'        => false, // can be hidden
771            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
772            'can_accept_tabs' => true,  // can/can't accept tabs onto it
773            'can_become_tab'  => false, // can be added as tab
774            'can_minimise'    => true, // can be minimised
775            'can_move'        => true, // can be moved
776
777        );
778
779        // call this
780        $this->initMetabox();
781    }
782
783    public function html( $invoice, $metabox ) {
784        ?>
785            <div class="zbs-generic-save-wrap">
786
787                    <div class="ui medium dividing header"><i class="save icon"></i> <?php esc_html_e( 'Invoice Actions', 'zero-bs-crm' ); ?></div>
788
789            <?php
790
791            // localise ID & content
792            $invoiceID = -1;
793            if ( is_array( $invoice ) && isset( $invoice['id'] ) ) {
794                $invoiceID = (int) $invoice['id'];
795            }
796
797            if ( $invoiceID > 0 ) { // existing
798
799                ?>
800
801                <?php do_action( 'zbs_invpro_itemlink' ); ?>
802
803                    <div class="clear"></div>
804
805
806                    <div class="zbs-invoice-actions-bottom zbs-objedit-actions-bottom">
807                                <button class="ui button black" type="button" id="zbs-edit-save"><?php esc_html_e( 'Update', 'zero-bs-crm' ); ?> <?php esc_html_e( 'Invoice', 'zero-bs-crm' ); ?></button>
808                    <?php
809
810                        #} Quick ver of this: http://themeflection.com/replace-wordpress-submit-meta-box/
811
812                    ?>
813                        <div id="zbs-invoice-actions-delete" class="zbs-objedit-actions-delete">
814                        <?php
815                        // for now just check if can modify invs, later better, granular perms.
816                        if ( zeroBSCRM_permsInvoices() ) {
817
818                            /*
819                                WP Deletion:
820                            no trash (at least v3.0)
821                            if ( !EMPTY_TRASH_DAYS )
822                            $delete_text = __('Delete Permanently', "zero-bs-crm");
823                            else
824                            $delete_text = __('Move to Trash', "zero-bs-crm");
825
826                            ?><a class="submitdelete deletion" href="<?php echo get_delete_post_link($post->ID); ?>"><?php echo $delete_text; ?></a><?php
827                            */
828
829                            $delete_text = __( 'Delete Permanently', 'zero-bs-crm' );
830                            ?>
831                                <a class="submitdelete deletion" href="<?php echo jpcrm_esc_link( 'delete', $invoiceID, 'invoice' ); ?>"><?php echo esc_html( $delete_text ); ?></a>
832                                <?php
833
834                        } //if
835                        ?>
836                        </div>
837                        
838                        <div class='clear'></div>
839
840                    </div>
841                <?php
842
843            } else {
844
845                ?>
846
847                <?php do_action( 'zbs_invpro_itemlink' ); ?>
848
849                            <button class="ui button black" type="button" id="zbs-edit-save"><?php esc_html_e( 'Save', 'zero-bs-crm' ); ?> <?php esc_html_e( 'Invoice', 'zero-bs-crm' ); ?></button>
850
851                <?php
852
853                    #} If it's a new post
854
855                    #} Gross hide :/
856
857            }
858
859            ?>
860            </div>
861            <?php
862            // / .zbs-generic-save-wrap
863    }
864
865    // saved via main metabox
866}