Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 298 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 4 |
| zeroBSCRM_InvoicesMetaboxSetup | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBS__InvoicePro | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBS__Metabox_Invoice | |
0.00% |
0 / 159 |
|
0.00% |
0 / 5 |
3422 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
2 | |||
| html | |
0.00% |
0 / 46 |
|
0.00% |
0 / 1 |
132 | |||
| save_data | |
0.00% |
0 / 88 |
|
0.00% |
0 / 1 |
1722 | |||
| post_save_data | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
20 | |||
| updateMessage | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBS__Metabox_InvoiceFiles | |
0.00% |
0 / 71 |
|
0.00% |
0 / 3 |
462 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
| html | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
56 | |||
| save_data | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
182 | |||
| zeroBS__Metabox_InvoiceTags | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBS__Metabox_InvoiceActions | |
0.00% |
0 / 40 |
|
0.00% |
0 / 2 |
42 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
2 | |||
| html | |
0.00% |
0 / 23 |
|
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 | ====================================================== */ |
| 16 | defined( 'ZEROBSCRM_PATH' ) || exit( 0 ); |
| 17 | /* |
| 18 | ====================================================== |
| 19 | / Breaking Checks |
| 20 | ====================================================== */ |
| 21 | |
| 22 | /* |
| 23 | ====================================================== |
| 24 | Init Func |
| 25 | ====================================================== */ |
| 26 | |
| 27 | function 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 | |
| 45 | add_action( 'admin_init', 'zeroBSCRM_InvoicesMetaboxSetup' ); |
| 46 | |
| 47 | /* |
| 48 | ====================================================== |
| 49 | / Init Func |
| 50 | ====================================================== */ |
| 51 | |
| 52 | /* |
| 53 | ====================================================== |
| 54 | Invoicing Metabox |
| 55 | ====================================================== */ |
| 56 | |
| 57 | class 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 | |
| 450 | class 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 | ====================================================== */ |
| 695 | function 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 | |
| 715 | class 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 | |
| 755 | class 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 | } |