Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 529 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
| zbsDAL_lineitems | |
0.00% |
0 / 529 |
|
0.00% |
0 / 11 |
32580 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
56 | |||
| getLineitem | |
0.00% |
0 / 60 |
|
0.00% |
0 / 1 |
650 | |||
| getLineitems | |
0.00% |
0 / 74 |
|
0.00% |
0 / 1 |
930 | |||
| getLineItemCount | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
56 | |||
| getLineitemsTaxSummary | |
0.00% |
0 / 45 |
|
0.00% |
0 / 1 |
420 | |||
| addUpdateLineitem | |
0.00% |
0 / 210 |
|
0.00% |
0 / 1 |
2756 | |||
| deleteLineitem | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
110 | |||
| deleteLineItemsForObject | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
182 | |||
| tidy_lineitem | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
42 | |||
| recalculate | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
90 | |||
| db_ready_lineitem | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | /* |
| 3 | * Jetpack CRM |
| 4 | * https://jetpackcrm.com |
| 5 | * V3.0+ |
| 6 | * |
| 7 | * Copyright 2020 Automattic |
| 8 | * |
| 9 | * Date: 14/01/19 |
| 10 | */ |
| 11 | |
| 12 | defined( 'ZEROBSCRM_PATH' ) || exit( 0 ); |
| 13 | |
| 14 | /* |
| 15 | Note on these getting separate DAL OBJ LAYER (excerpt from WH DAL3.0 notes) |
| 16 | - There are a few tables which are nested (no separate DAL2.Obj file), for these reasons: |
| 17 | - Meta, Custom Fields, ObjLinks are used so universally that they have been left in main DAL2.php |
| 18 | - Event Reminders sit within Events.php obj layer file |
| 19 | - LineItems get their own file, while they could sit in DAL2, they're new, and using the object layer will speed up the code write (easy get funcs etc. using obj model). Arguably these + meta, custom fields, etc. could sit somewhere else (these are used for Quotes, Invs, Trans) |
| 20 | */ |
| 21 | |
| 22 | /** |
| 23 | * ZBS DAL >> LineItems |
| 24 | * |
| 25 | * @author Woody Hayday <hello@jetpackcrm.com> |
| 26 | * @version 2.0 |
| 27 | * @access public |
| 28 | * @see https://jetpackcrm.com/kb |
| 29 | */ |
| 30 | class zbsDAL_lineitems extends zbsDAL_ObjectLayer { |
| 31 | |
| 32 | protected $objectType = ZBS_TYPE_LINEITEM; |
| 33 | protected $objectDBPrefix = 'zbsli_'; |
| 34 | protected $objectModel = array( |
| 35 | |
| 36 | // ID |
| 37 | 'ID' => array( |
| 38 | 'fieldname' => 'ID', |
| 39 | 'format' => 'int', |
| 40 | ), |
| 41 | |
| 42 | // site + team generics |
| 43 | 'zbs_site' => array( |
| 44 | 'fieldname' => 'zbs_site', |
| 45 | 'format' => 'int', |
| 46 | ), |
| 47 | 'zbs_team' => array( |
| 48 | 'fieldname' => 'zbs_team', |
| 49 | 'format' => 'int', |
| 50 | ), |
| 51 | 'zbs_owner' => array( |
| 52 | 'fieldname' => 'zbs_owner', |
| 53 | 'format' => 'int', |
| 54 | ), |
| 55 | |
| 56 | // other fields |
| 57 | 'order' => array( |
| 58 | 'fieldname' => 'zbsli_order', |
| 59 | 'format' => 'int', |
| 60 | ), |
| 61 | 'title' => array( |
| 62 | 'fieldname' => 'zbsli_title', |
| 63 | 'format' => 'str', |
| 64 | 'max_len' => 300, |
| 65 | ), |
| 66 | 'desc' => array( |
| 67 | 'fieldname' => 'zbsli_desc', |
| 68 | 'format' => 'str', |
| 69 | 'max_len' => 300, |
| 70 | ), |
| 71 | 'quantity' => array( |
| 72 | 'fieldname' => 'zbsli_quantity', |
| 73 | 'format' => 'decimal', |
| 74 | ), |
| 75 | 'price' => array( |
| 76 | 'fieldname' => 'zbsli_price', |
| 77 | 'format' => 'decimal', |
| 78 | ), |
| 79 | 'currency' => array( |
| 80 | 'fieldname' => 'zbsli_currency', |
| 81 | 'format' => 'curr', |
| 82 | ), |
| 83 | 'net' => array( |
| 84 | 'fieldname' => 'zbsli_net', |
| 85 | 'format' => 'decimal', |
| 86 | ), |
| 87 | 'discount' => array( |
| 88 | 'fieldname' => 'zbsli_discount', |
| 89 | 'format' => 'decimal', |
| 90 | ), |
| 91 | 'fee' => array( |
| 92 | 'fieldname' => 'zbsli_fee', |
| 93 | 'format' => 'decimal', |
| 94 | ), |
| 95 | 'shipping' => array( |
| 96 | 'fieldname' => 'zbsli_shipping', |
| 97 | 'format' => 'decimal', |
| 98 | ), |
| 99 | 'shipping_taxes' => array( |
| 100 | 'fieldname' => 'zbsli_shipping_taxes', |
| 101 | 'format' => 'str', |
| 102 | ), |
| 103 | 'shipping_tax' => array( |
| 104 | 'fieldname' => 'zbsli_shipping_tax', |
| 105 | 'format' => 'decimal', |
| 106 | ), |
| 107 | 'taxes' => array( |
| 108 | 'fieldname' => 'zbsli_taxes', |
| 109 | 'format' => 'str', |
| 110 | ), |
| 111 | 'tax' => array( |
| 112 | 'fieldname' => 'zbsli_tax', |
| 113 | 'format' => 'decimal', |
| 114 | ), |
| 115 | 'total' => array( |
| 116 | 'fieldname' => 'zbsli_total', |
| 117 | 'format' => 'decimal', |
| 118 | ), |
| 119 | 'created' => array( |
| 120 | 'fieldname' => 'zbsli_created', |
| 121 | 'format' => 'uts', |
| 122 | ), |
| 123 | 'lastupdated' => array( |
| 124 | 'fieldname' => 'zbsli_lastupdated', |
| 125 | 'format' => 'uts', |
| 126 | ), |
| 127 | |
| 128 | ); |
| 129 | |
| 130 | function __construct( $args = array() ) { |
| 131 | |
| 132 | #} =========== LOAD ARGS ============== |
| 133 | $defaultArgs = array( |
| 134 | |
| 135 | // 'tag' => false, |
| 136 | |
| 137 | ); |
| 138 | foreach ( $defaultArgs as $argK => $argV ) { |
| 139 | $this->$argK = $argV; |
| 140 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 141 | if ( is_array( $args[ $argK ] ) ) { |
| 142 | $newData = $this->$argK; |
| 143 | if ( ! is_array( $newData ) ) { |
| 144 | $newData = array(); |
| 145 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 146 | $newData[ $subK ] = $subV; |
| 147 | }$this->$argK = $newData; |
| 148 | } else { |
| 149 | $this->$argK = $args[ $argK ]; } |
| 150 | } |
| 151 | } |
| 152 | #} =========== / LOAD ARGS ============= |
| 153 | } |
| 154 | |
| 155 | // =============================================================================== |
| 156 | // =========== LINEITEM ======================================================= |
| 157 | |
| 158 | /** |
| 159 | * returns full lineitem line +- details |
| 160 | * |
| 161 | * @param int id lineitem id |
| 162 | * @param array $args Associative array of arguments |
| 163 | * |
| 164 | * @return array lineitem object |
| 165 | */ |
| 166 | public function getLineitem( $id = -1, $args = array() ) { |
| 167 | |
| 168 | global $zbs; |
| 169 | |
| 170 | #} =========== LOAD ARGS ============== |
| 171 | $defaultArgs = array( |
| 172 | |
| 173 | // permissions |
| 174 | 'ignoreowner' => false, // this'll let you not-check the owner of obj |
| 175 | |
| 176 | // returns scalar ID of line |
| 177 | 'onlyID' => false, |
| 178 | |
| 179 | 'fields' => false, // false = *, array = fieldnames |
| 180 | |
| 181 | ); |
| 182 | foreach ( $defaultArgs as $argK => $argV ) { |
| 183 | $$argK = $argV; |
| 184 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 185 | if ( is_array( $args[ $argK ] ) ) { |
| 186 | $newData = $$argK; |
| 187 | if ( ! is_array( $newData ) ) { |
| 188 | $newData = array(); |
| 189 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 190 | $newData[ $subK ] = $subV; |
| 191 | }$$argK = $newData; |
| 192 | } else { |
| 193 | $$argK = $args[ $argK ]; } |
| 194 | } |
| 195 | } |
| 196 | #} =========== / LOAD ARGS ============= |
| 197 | |
| 198 | #} Check ID |
| 199 | $id = (int) $id; |
| 200 | if ( |
| 201 | ( ! empty( $id ) && $id > 0 ) |
| 202 | || |
| 203 | ( ! empty( $email ) ) |
| 204 | || |
| 205 | ( ! empty( $externalSource ) && ! empty( $externalSourceUID ) ) |
| 206 | ) { |
| 207 | |
| 208 | global $ZBSCRM_t, $wpdb; |
| 209 | $wheres = array( 'direct' => array() ); |
| 210 | $whereStr = ''; |
| 211 | $additionalWhere = ''; |
| 212 | $params = array(); |
| 213 | $res = array(); |
| 214 | $extraSelect = ''; |
| 215 | |
| 216 | #} ============= PRE-QUERY ============ |
| 217 | |
| 218 | $selector = 'lineitem.*'; |
| 219 | if ( is_array( $fields ) ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable |
| 220 | $selector = ''; |
| 221 | |
| 222 | // always needs id, so add if not present |
| 223 | if ( ! in_array( 'ID', $fields ) ) { |
| 224 | $selector = 'lineitem.ID'; |
| 225 | } |
| 226 | |
| 227 | foreach ( $fields as $f ) { |
| 228 | if ( ! empty( $selector ) ) { |
| 229 | $selector .= ','; |
| 230 | } |
| 231 | $selector .= 'lineitem.' . $f; |
| 232 | } |
| 233 | } elseif ( $onlyID ) { |
| 234 | $selector = 'lineitem.ID'; |
| 235 | } |
| 236 | |
| 237 | #} ============ / PRE-QUERY =========== |
| 238 | |
| 239 | #} Build query |
| 240 | $query = 'SELECT ' . $selector . $extraSelect . ' FROM ' . $ZBSCRM_t['lineitems'] . ' as lineitem'; |
| 241 | #} ============= WHERE ================ |
| 242 | |
| 243 | if ( ! empty( $id ) && $id > 0 ) { |
| 244 | |
| 245 | #} Add ID |
| 246 | $wheres['ID'] = array( 'ID', '=', '%d', $id ); |
| 247 | |
| 248 | } |
| 249 | |
| 250 | #} ============ / WHERE ============== |
| 251 | |
| 252 | #} Build out any WHERE clauses |
| 253 | $wheresArr = $this->buildWheres( $wheres, $whereStr, $params ); |
| 254 | $whereStr = $wheresArr['where']; |
| 255 | $params = $params + $wheresArr['params']; |
| 256 | #} / Build WHERE |
| 257 | |
| 258 | #} Ownership v1.0 - the following adds SITE + TEAM checks, and (optionally), owner |
| 259 | $params = array_merge( $params, $this->ownershipQueryVars( $ignoreowner ) ); // merges in any req. |
| 260 | $ownQ = $this->ownershipSQL( $ignoreowner ); |
| 261 | if ( ! empty( $ownQ ) ) { |
| 262 | $additionalWhere = $this->spaceAnd( $additionalWhere ) . $ownQ; // adds str to query |
| 263 | } |
| 264 | #} / Ownership |
| 265 | |
| 266 | #} Append to sql (this also automatically deals with sortby and paging) |
| 267 | $query .= $this->buildWhereStr( $whereStr, $additionalWhere ) . $this->buildSort( 'ID', 'DESC' ) . $this->buildPaging( 0, 1 ); |
| 268 | |
| 269 | try { |
| 270 | |
| 271 | #} Prep & run query |
| 272 | $queryObj = $this->prepare( $query, $params ); |
| 273 | $potentialRes = $wpdb->get_row( $queryObj, OBJECT ); |
| 274 | |
| 275 | } catch ( Exception $e ) { |
| 276 | |
| 277 | #} General SQL Err |
| 278 | $this->catchSQLError( $e ); |
| 279 | |
| 280 | } |
| 281 | |
| 282 | #} Interpret Results (ROW) |
| 283 | if ( isset( $potentialRes ) && isset( $potentialRes->ID ) ) { |
| 284 | |
| 285 | #} Has results, tidy + return |
| 286 | |
| 287 | #} Only ID? return it directly |
| 288 | if ( $onlyID ) { |
| 289 | return $potentialRes->ID; |
| 290 | } |
| 291 | |
| 292 | // tidy |
| 293 | if ( is_array( $fields ) ) { |
| 294 | // guesses fields based on table col names |
| 295 | $res = $this->lazyTidyGeneric( $potentialRes ); |
| 296 | } else { |
| 297 | // proper tidy |
| 298 | $res = $this->tidy_lineitem( $potentialRes, $withCustomFields ); |
| 299 | } |
| 300 | |
| 301 | return $res; |
| 302 | |
| 303 | } |
| 304 | } // / if ID |
| 305 | |
| 306 | return false; |
| 307 | } |
| 308 | |
| 309 | /** |
| 310 | * returns lineitem detail lines |
| 311 | * |
| 312 | * @param array $args Associative array of arguments |
| 313 | * |
| 314 | * @return array of lineitem lines |
| 315 | */ |
| 316 | public function getLineitems( $args = array() ) { |
| 317 | |
| 318 | global $zbs; |
| 319 | |
| 320 | #} ============ LOAD ARGS ============= |
| 321 | $defaultArgs = array( |
| 322 | |
| 323 | // Search/Filtering (leave as false to ignore) |
| 324 | 'searchPhrase' => '', // searches zbsli_title, zbsli_desc |
| 325 | 'associatedObjType' => false, // e.g. ZBS_TYPE_QUOTE |
| 326 | 'associatedObjID' => false, // e.g. 123 |
| 327 | // Note on associated types: They can be used: |
| 328 | // associatedObjType |
| 329 | // associatedObjType + associatedObjID |
| 330 | // BUT NOT JUST associatedObjID (would bring collisions) |
| 331 | |
| 332 | 'withCustomFields' => false, // none yet anyhow |
| 333 | |
| 334 | // returns |
| 335 | 'count' => false, |
| 336 | |
| 337 | 'sortByField' => 'ID', |
| 338 | 'sortOrder' => 'ASC', |
| 339 | 'page' => 0, // this is what page it is (gets * by for limit) |
| 340 | 'perPage' => 100, |
| 341 | 'whereCase' => 'AND', // DEFAULT = AND |
| 342 | |
| 343 | // permissions |
| 344 | 'ignoreowner' => false, // this'll let you not-check the owner of obj |
| 345 | |
| 346 | ); |
| 347 | foreach ( $defaultArgs as $argK => $argV ) { |
| 348 | $$argK = $argV; |
| 349 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 350 | if ( is_array( $args[ $argK ] ) ) { |
| 351 | $newData = $$argK; |
| 352 | if ( ! is_array( $newData ) ) { |
| 353 | $newData = array(); |
| 354 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 355 | $newData[ $subK ] = $subV; |
| 356 | }$$argK = $newData; |
| 357 | } else { |
| 358 | $$argK = $args[ $argK ]; } |
| 359 | } |
| 360 | } |
| 361 | #} =========== / LOAD ARGS ============= |
| 362 | |
| 363 | global $ZBSCRM_t, $wpdb, $zbs; |
| 364 | $wheres = array( 'direct' => array() ); |
| 365 | $whereStr = ''; |
| 366 | $additionalWhere = ''; |
| 367 | $params = array(); |
| 368 | $res = array(); |
| 369 | $joinQ = ''; |
| 370 | $extraSelect = ''; |
| 371 | |
| 372 | #} ============= PRE-QUERY ============ |
| 373 | |
| 374 | #} Capitalise this |
| 375 | $sortOrder = strtoupper( $sortOrder ); |
| 376 | |
| 377 | #} If just count, turn off any extra gumpf |
| 378 | // if ($count) { } |
| 379 | |
| 380 | #} ============ / PRE-QUERY =========== |
| 381 | |
| 382 | #} Build query |
| 383 | $query = 'SELECT lineitem.*' . $extraSelect . ' FROM ' . $ZBSCRM_t['lineitems'] . ' as lineitem' . $joinQ; |
| 384 | |
| 385 | #} Count override |
| 386 | if ( $count ) { |
| 387 | $query = 'SELECT COUNT(lineitem.ID) FROM ' . $ZBSCRM_t['lineitems'] . ' as lineitem' . $joinQ; |
| 388 | } |
| 389 | |
| 390 | #} ============= WHERE ================ |
| 391 | |
| 392 | #} Add Search phrase |
| 393 | if ( ! empty( $searchPhrase ) ) { |
| 394 | |
| 395 | // search? - ALL THESE COLS should probs have index of FULLTEXT in db? |
| 396 | $searchWheres = array(); |
| 397 | $searchWheres['search_title'] = array( 'zbsli_title', 'LIKE', '%s', '%' . $searchPhrase . '%' ); |
| 398 | $searchWheres['search_desc'] = array( 'zbsli_desc', 'LIKE', '%s', '%' . $searchPhrase . '%' ); |
| 399 | |
| 400 | // This generates a query like 'zbsli_fname LIKE %s OR zbsli_lname LIKE %s', |
| 401 | // which we then need to include as direct subquery (below) in main query :) |
| 402 | $searchQueryArr = $this->buildWheres( $searchWheres, '', array(), 'OR', false ); |
| 403 | |
| 404 | if ( is_array( $searchQueryArr ) && isset( $searchQueryArr['where'] ) && ! empty( $searchQueryArr['where'] ) ) { |
| 405 | |
| 406 | // add it |
| 407 | $wheres['direct'][] = array( '(' . $searchQueryArr['where'] . ')', $searchQueryArr['params'] ); |
| 408 | |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | // associated object search |
| 413 | // simplifiers |
| 414 | $hasObjType = false; |
| 415 | $hasObjID = false; |
| 416 | if ( ! empty( $associatedObjType ) && $associatedObjType > 0 ) { |
| 417 | $hasObjType = true; |
| 418 | } |
| 419 | if ( ! empty( $associatedObjID ) && $associatedObjID > 0 ) { |
| 420 | $hasObjID = true; |
| 421 | } |
| 422 | |
| 423 | // switch depending on setup |
| 424 | if ( $hasObjType && $hasObjID ) { |
| 425 | |
| 426 | // has id + type to match to (e.g. quote 123) |
| 427 | $wheres['associatedObjType'] = array( 'ID', 'IN', '(SELECT zbsol_objid_from FROM ' . $ZBSCRM_t['objlinks'] . ' WHERE zbsol_objtype_from = ' . ZBS_TYPE_LINEITEM . ' AND zbsol_objtype_to = %d AND zbsol_objid_to = %d)', array( $associatedObjType, $associatedObjID ) ); |
| 428 | |
| 429 | } elseif ( $hasObjType && ! $hasObjID ) { |
| 430 | |
| 431 | // has type but no id |
| 432 | // e.g. line items attached to invoices |
| 433 | $wheres['associatedObjType'] = array( 'ID', 'IN', '(SELECT zbsol_objid_from FROM ' . $ZBSCRM_t['objlinks'] . ' WHERE zbsol_objtype_from = ' . ZBS_TYPE_LINEITEM . ' AND zbsol_objtype_to = %d)', $associatedObjType ); |
| 434 | |
| 435 | } elseif ( $hasObjID && ! $hasObjType ) { |
| 436 | |
| 437 | // has id but no type |
| 438 | // DO NOTHING, this is dodgy to ever call :) as collision of objs |
| 439 | } |
| 440 | |
| 441 | #} ============ / WHERE =============== |
| 442 | |
| 443 | #} CHECK this + reset to default if faulty |
| 444 | if ( ! in_array( $whereCase, array( 'AND', 'OR' ) ) ) { |
| 445 | $whereCase = 'AND'; |
| 446 | } |
| 447 | |
| 448 | #} Build out any WHERE clauses |
| 449 | $wheresArr = $this->buildWheres( $wheres, $whereStr, $params, $whereCase ); |
| 450 | $whereStr = $wheresArr['where']; |
| 451 | $params = $params + $wheresArr['params']; |
| 452 | #} / Build WHERE |
| 453 | |
| 454 | #} Ownership v1.0 - the following adds SITE + TEAM checks, and (optionally), owner |
| 455 | $params = array_merge( $params, $this->ownershipQueryVars( $ignoreowner ) ); // merges in any req. |
| 456 | $ownQ = $this->ownershipSQL( $ignoreowner, 'contact' ); |
| 457 | if ( ! empty( $ownQ ) ) { |
| 458 | $additionalWhere = $this->spaceAnd( $additionalWhere ) . $ownQ; // adds str to query |
| 459 | } |
| 460 | #} / Ownership |
| 461 | |
| 462 | #} Append to sql (this also automatically deals with sortby and paging) |
| 463 | $query .= $this->buildWhereStr( $whereStr, $additionalWhere ) . $this->buildSort( $sortByField, $sortOrder ) . $this->buildPaging( $page, $perPage ); |
| 464 | |
| 465 | try { |
| 466 | |
| 467 | #} Prep & run query |
| 468 | $queryObj = $this->prepare( $query, $params ); |
| 469 | |
| 470 | #} Catch count + return if requested |
| 471 | if ( $count ) { |
| 472 | return $wpdb->get_var( $queryObj ); |
| 473 | } |
| 474 | |
| 475 | #} else continue.. |
| 476 | $potentialRes = $wpdb->get_results( $queryObj, OBJECT ); |
| 477 | |
| 478 | } catch ( Exception $e ) { |
| 479 | |
| 480 | #} General SQL Err |
| 481 | $this->catchSQLError( $e ); |
| 482 | |
| 483 | } |
| 484 | |
| 485 | #} Interpret results (Result Set - multi-row) |
| 486 | if ( isset( $potentialRes ) && is_array( $potentialRes ) && count( $potentialRes ) > 0 ) { |
| 487 | |
| 488 | #} Has results, tidy + return |
| 489 | foreach ( $potentialRes as $resDataLine ) { |
| 490 | |
| 491 | // tidy |
| 492 | $resArr = $this->tidy_lineitem( $resDataLine, $withCustomFields ); |
| 493 | |
| 494 | $res[] = $resArr; |
| 495 | |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | return $res; |
| 500 | } |
| 501 | |
| 502 | /** |
| 503 | * Returns a count of lineitems (owned) |
| 504 | * .. inc by status |
| 505 | * |
| 506 | * @return int count |
| 507 | */ |
| 508 | public function getLineItemCount( $args = array() ) { |
| 509 | |
| 510 | #} ============ LOAD ARGS ============= |
| 511 | $defaultArgs = array( |
| 512 | |
| 513 | // Search/Filtering (leave as false to ignore) |
| 514 | |
| 515 | // permissions |
| 516 | 'ignoreowner' => true, // this'll let you not-check the owner of obj |
| 517 | |
| 518 | ); |
| 519 | foreach ( $defaultArgs as $argK => $argV ) { |
| 520 | $$argK = $argV; |
| 521 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 522 | if ( is_array( $args[ $argK ] ) ) { |
| 523 | $newData = $$argK; |
| 524 | if ( ! is_array( $newData ) ) { |
| 525 | $newData = array(); |
| 526 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 527 | $newData[ $subK ] = $subV; |
| 528 | }$$argK = $newData; |
| 529 | } else { |
| 530 | $$argK = $args[ $argK ]; } |
| 531 | } |
| 532 | } |
| 533 | #} =========== / LOAD ARGS ============= |
| 534 | |
| 535 | $whereArr = array(); |
| 536 | |
| 537 | return $this->DAL()->getFieldByWHERE( |
| 538 | array( |
| 539 | 'objtype' => ZBS_TYPE_LINEITEM, |
| 540 | 'colname' => 'COUNT(ID)', |
| 541 | 'where' => $whereArr, |
| 542 | 'ignoreowner' => $ignoreowner, |
| 543 | ) |
| 544 | ); |
| 545 | } |
| 546 | |
| 547 | /** |
| 548 | * returns tax summary array, of taxes applicable to given lineitems |
| 549 | * |
| 550 | * @param array $args Associative array of arguments |
| 551 | * |
| 552 | * @return array summary of taxes (e.g. array(array('name' => 'VAT','rate' => 20, 'value' => 123.44))) |
| 553 | */ |
| 554 | public function getLineitemsTaxSummary( $args = array() ) { |
| 555 | |
| 556 | global $zbs; |
| 557 | |
| 558 | #} ============ LOAD ARGS ============= |
| 559 | $defaultArgs = array( |
| 560 | |
| 561 | // pass lineitems objs in array |
| 562 | 'lineItems' => array(), |
| 563 | |
| 564 | ); |
| 565 | foreach ( $defaultArgs as $argK => $argV ) { |
| 566 | $$argK = $argV; |
| 567 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 568 | if ( is_array( $args[ $argK ] ) ) { |
| 569 | $newData = $$argK; |
| 570 | if ( ! is_array( $newData ) ) { |
| 571 | $newData = array(); |
| 572 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 573 | $newData[ $subK ] = $subV; |
| 574 | }$$argK = $newData; |
| 575 | } else { |
| 576 | $$argK = $args[ $argK ]; } |
| 577 | } |
| 578 | } |
| 579 | #} =========== / LOAD ARGS ============= |
| 580 | |
| 581 | $summaryData = array(); |
| 582 | |
| 583 | // calc |
| 584 | if ( is_array( $lineItems ) && count( $lineItems ) > 0 ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable,WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase |
| 585 | |
| 586 | $lineItemTaxes = array(); |
| 587 | $taxRateTable = zeroBSCRM_taxRates_getTaxTableArr( true ); |
| 588 | |
| 589 | foreach ( $lineItems as $lineItem ) { |
| 590 | |
| 591 | // got any taxes on ? |
| 592 | if ( isset( $lineItem['net'] ) && isset( $lineItem['taxes'] ) ) { |
| 593 | |
| 594 | $taxRatesToApply = array(); |
| 595 | |
| 596 | // get any taxes... |
| 597 | if ( strpos( $lineItem['taxes'], ',' ) ) { |
| 598 | |
| 599 | $taxRateIDs = explode( ',', $lineItem['taxes'] ); |
| 600 | if ( is_array( $taxRateIDs ) ) { |
| 601 | $taxRatesToApply = $taxRateIDs; |
| 602 | } |
| 603 | } else { |
| 604 | $taxRatesToApply[] = (int) $lineItem['taxes']; |
| 605 | } |
| 606 | |
| 607 | // calc these ones + add to summary |
| 608 | if ( is_array( $taxRatesToApply ) ) { |
| 609 | foreach ( $taxRatesToApply as $taxRateID ) { |
| 610 | |
| 611 | $rateID = (int) $taxRateID; |
| 612 | if ( isset( $taxRateTable[ $rateID ] ) ) { |
| 613 | |
| 614 | // get rate |
| 615 | $rate = 0.0; |
| 616 | if ( isset( $taxRateTable[ $rateID ]['rate'] ) ) { |
| 617 | $rate = (float) $taxRateTable[ $rateID ]['rate']; |
| 618 | } |
| 619 | |
| 620 | // calc + add |
| 621 | $itemNet = $lineItem['net']; |
| 622 | if ( isset( $lineItem['discount'] ) ) { |
| 623 | $itemNet -= $lineItem['discount']; |
| 624 | } |
| 625 | $taxValue = round( $itemNet * ( $rate / 100 ), 2 ); |
| 626 | |
| 627 | // add to summary |
| 628 | if ( ! isset( $summaryData[ $rateID ] ) ) { |
| 629 | |
| 630 | // new, add |
| 631 | $summaryData[ $rateID ] = array( |
| 632 | |
| 633 | 'name' => $taxRateTable[ $rateID ]['name'], |
| 634 | 'rate' => $rate, |
| 635 | 'value' => $taxValue, |
| 636 | |
| 637 | ); |
| 638 | |
| 639 | } else { |
| 640 | |
| 641 | // += |
| 642 | $summaryData[ $rateID ]['value'] += $taxValue; |
| 643 | |
| 644 | } |
| 645 | } // else not set? |
| 646 | |
| 647 | } // / foreach tax rate to apply |
| 648 | } |
| 649 | } // / if has net and taxes |
| 650 | |
| 651 | } // / foreach line item |
| 652 | |
| 653 | } // / if has items |
| 654 | |
| 655 | return $summaryData; |
| 656 | } |
| 657 | |
| 658 | /** |
| 659 | * adds or updates a lineitem object |
| 660 | * |
| 661 | * @param array $args Associative array of arguments |
| 662 | * id (if update), owner, data (array of field data) |
| 663 | * |
| 664 | * @return int line ID |
| 665 | */ |
| 666 | public function addUpdateLineitem( $args = array() ) { |
| 667 | |
| 668 | global $ZBSCRM_t, $wpdb, $zbs; |
| 669 | |
| 670 | #} ============ LOAD ARGS ============= |
| 671 | $defaultArgs = array( |
| 672 | |
| 673 | 'id' => -1, |
| 674 | 'owner' => -1, |
| 675 | |
| 676 | // assign to |
| 677 | 'linkedObjType' => -1, |
| 678 | 'linkedObjID' => -1, |
| 679 | |
| 680 | // fields (directly) |
| 681 | 'data' => array( |
| 682 | |
| 683 | 'order' => '', |
| 684 | 'title' => '', |
| 685 | 'desc' => '', |
| 686 | 'quantity' => '', |
| 687 | 'price' => '', |
| 688 | 'currency' => '', |
| 689 | 'net' => '', |
| 690 | 'discount' => '', |
| 691 | 'fee' => '', |
| 692 | 'shipping' => '', |
| 693 | 'shipping_taxes' => '', |
| 694 | 'shipping_tax' => '', |
| 695 | 'taxes' => '', |
| 696 | 'tax' => '', |
| 697 | 'total' => '', |
| 698 | 'lastupdated' => '', |
| 699 | |
| 700 | // allow this to be set for sync etc. |
| 701 | 'created' => -1, |
| 702 | |
| 703 | ), |
| 704 | |
| 705 | 'limitedFields' => -1, // if this is set it OVERRIDES data (allowing you to set specific fields + leave rest in tact) |
| 706 | // ^^ will look like: array(array('key'=>x,'val'=>y,'type'=>'%s')) |
| 707 | |
| 708 | 'silentInsert' => false, // this was for init Migration - it KILLS all IA for newLineitem (because is migrating, not creating new :) this was -1 before |
| 709 | |
| 710 | 'do_not_update_blanks' => false, // this allows you to not update fields if blank (same as fieldoverride for extsource -> in) |
| 711 | |
| 712 | 'calculate_totals' => false, // This allows us to recalculate tax, subtotal, total via php (e.g. if added via api). Only works if not using limitedFields |
| 713 | |
| 714 | ); |
| 715 | foreach ( $defaultArgs as $argK => $argV ) { |
| 716 | $$argK = $argV; |
| 717 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 718 | if ( is_array( $args[ $argK ] ) ) { |
| 719 | $newData = $$argK; |
| 720 | if ( ! is_array( $newData ) ) { |
| 721 | $newData = array(); |
| 722 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 723 | $newData[ $subK ] = $subV; |
| 724 | }$$argK = $newData; |
| 725 | } else { |
| 726 | $$argK = $args[ $argK ]; } |
| 727 | } |
| 728 | } |
| 729 | |
| 730 | #} =========== / LOAD ARGS ============ |
| 731 | |
| 732 | #} ========== CHECK FIELDS ============ |
| 733 | |
| 734 | $id = (int) $id; |
| 735 | |
| 736 | // here we check that the potential owner CAN even own |
| 737 | if ( $owner > 0 && ! user_can( $owner, 'admin_zerobs_usr' ) ) { |
| 738 | $owner = -1; |
| 739 | } |
| 740 | |
| 741 | // if owner = -1, add current |
| 742 | if ( ! isset( $owner ) || $owner === -1 ) { |
| 743 | $owner = zeroBSCRM_user(); } |
| 744 | |
| 745 | if ( is_array( $limitedFields ) ) { |
| 746 | |
| 747 | // LIMITED UPDATE (only a few fields.) |
| 748 | if ( ! is_array( $limitedFields ) || count( $limitedFields ) <= 0 ) { |
| 749 | return false; |
| 750 | } |
| 751 | // REQ. ID too (can only update) |
| 752 | if ( empty( $id ) || $id <= 0 ) { |
| 753 | return false; |
| 754 | } |
| 755 | } else { |
| 756 | |
| 757 | // NORMAL, FULL UPDATE |
| 758 | |
| 759 | // (re)calculate the totals etc? |
| 760 | if ( $calculate_totals ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable |
| 761 | |
| 762 | $data = $this->recalculate( $data ); |
| 763 | |
| 764 | } |
| 765 | } |
| 766 | |
| 767 | #} ========= / CHECK FIELDS =========== |
| 768 | |
| 769 | #} ========= OVERRIDE SETTING (Deny blank overrides) =========== |
| 770 | |
| 771 | // either ext source + setting, or set by the func call |
| 772 | if ( $do_not_update_blanks ) { |
| 773 | |
| 774 | // this setting says 'don't override filled-out data with blanks' |
| 775 | // so here we check through any passed blanks + convert to limitedFields |
| 776 | // only matters if $id is set (there is somt to update not add |
| 777 | if ( isset( $id ) && ! empty( $id ) && $id > 0 ) { |
| 778 | |
| 779 | // get data to copy over (for now, this is required to remove 'fullname' etc.) |
| 780 | $dbData = $this->db_ready_lineitem( $data ); |
| 781 | // unset($dbData['id']); // this is unset because we use $id, and is update, so not req. legacy issue |
| 782 | // unset($dbData['created']); // this is unset because this uses an obj which has been 'updated' against original details, where created is output in the WRONG format :) |
| 783 | |
| 784 | $origData = $data; // $data = array(); |
| 785 | $limitedData = array(); // array(array('key'=>'zbsli_x','val'=>y,'type'=>'%s')) |
| 786 | |
| 787 | // cycle through + translate into limitedFields (removing any blanks, or arrays (e.g. externalSources)) |
| 788 | // we also have to remake a 'faux' data (removing blanks for tags etc.) for the post-update updates |
| 789 | foreach ( $dbData as $k => $v ) { |
| 790 | |
| 791 | $intV = (int) $v; |
| 792 | |
| 793 | // only add if valuenot empty |
| 794 | if ( ! is_array( $v ) && ! empty( $v ) && $v != '' && $v !== 0 && $v !== -1 && $intV !== -1 ) { |
| 795 | |
| 796 | // add to update arr |
| 797 | $limitedData[] = array( |
| 798 | 'key' => 'zbsli_' . $k, // we have to add zbsli_ here because translating from data -> limited fields |
| 799 | 'val' => $v, |
| 800 | 'type' => $this->getTypeStr( 'zbsli_' . $k ), |
| 801 | ); |
| 802 | |
| 803 | // add to remade $data for post-update updates |
| 804 | $data[ $k ] = $v; |
| 805 | |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | // copy over |
| 810 | $limitedFields = $limitedData; |
| 811 | |
| 812 | } // / if ID |
| 813 | |
| 814 | } // / if do_not_update_blanks |
| 815 | |
| 816 | #} ========= / OVERRIDE SETTING (Deny blank overrides) =========== |
| 817 | |
| 818 | #} ========= BUILD DATA =========== |
| 819 | |
| 820 | $update = false; |
| 821 | $dataArr = array(); |
| 822 | $typeArr = array(); |
| 823 | |
| 824 | if ( is_array( $limitedFields ) ) { |
| 825 | |
| 826 | // LIMITED FIELDS |
| 827 | $update = true; |
| 828 | |
| 829 | // cycle through |
| 830 | foreach ( $limitedFields as $field ) { |
| 831 | |
| 832 | // some weird case where getting empties, so added check |
| 833 | if ( ! empty( $field['key'] ) ) { |
| 834 | $dataArr[ $field['key'] ] = $field['val']; |
| 835 | $typeArr[] = $field['type']; |
| 836 | } |
| 837 | } |
| 838 | |
| 839 | // add update time |
| 840 | if ( ! isset( $dataArr['zbsli_lastupdated'] ) ) { |
| 841 | $dataArr['zbsli_lastupdated'] = time(); |
| 842 | $typeArr[] = '%d'; } |
| 843 | } else { |
| 844 | |
| 845 | // FULL UPDATE/INSERT |
| 846 | |
| 847 | // UPDATE |
| 848 | $dataArr = array( |
| 849 | |
| 850 | // ownership |
| 851 | // no need to update these (as of yet) - can't move teams etc. |
| 852 | // 'zbs_site' => zeroBSCRM_installSite(), |
| 853 | // 'zbs_team' => zeroBSCRM_installTeam(), |
| 854 | // 'zbs_owner' => $owner, |
| 855 | |
| 856 | 'zbsli_order' => $data['order'], |
| 857 | 'zbsli_title' => $data['title'], |
| 858 | 'zbsli_desc' => $data['desc'], |
| 859 | 'zbsli_quantity' => $data['quantity'], |
| 860 | 'zbsli_price' => $data['price'], |
| 861 | 'zbsli_currency' => $data['currency'], |
| 862 | 'zbsli_net' => $data['net'], |
| 863 | 'zbsli_discount' => $data['discount'], |
| 864 | 'zbsli_fee' => $data['fee'], |
| 865 | 'zbsli_shipping' => $data['shipping'], |
| 866 | 'zbsli_shipping_taxes' => $data['shipping_taxes'], |
| 867 | 'zbsli_shipping_tax' => $data['shipping_tax'], |
| 868 | 'zbsli_taxes' => $data['taxes'], |
| 869 | 'zbsli_tax' => $data['tax'], |
| 870 | 'zbsli_total' => $data['total'], |
| 871 | 'zbsli_lastupdated' => time(), |
| 872 | |
| 873 | ); |
| 874 | |
| 875 | $typeArr = array( // field data types |
| 876 | // '%d', // site |
| 877 | // '%d', // team |
| 878 | // '%d', // owner |
| 879 | |
| 880 | '%d', |
| 881 | '%s', |
| 882 | '%s', |
| 883 | '%f', |
| 884 | '%s', |
| 885 | '%s', |
| 886 | '%s', |
| 887 | '%s', |
| 888 | '%s', |
| 889 | '%s', |
| 890 | '%s', |
| 891 | '%s', |
| 892 | '%s', |
| 893 | '%s', |
| 894 | '%s', |
| 895 | '%d', |
| 896 | |
| 897 | ); |
| 898 | |
| 899 | if ( ! empty( $id ) && $id > 0 ) { |
| 900 | |
| 901 | // is update |
| 902 | $update = true; |
| 903 | |
| 904 | } else { |
| 905 | |
| 906 | // INSERT (get's few extra :D) |
| 907 | $update = false; |
| 908 | $dataArr['zbs_site'] = zeroBSCRM_site(); |
| 909 | $typeArr[] = '%d'; |
| 910 | $dataArr['zbs_team'] = zeroBSCRM_team(); |
| 911 | $typeArr[] = '%d'; |
| 912 | $dataArr['zbs_owner'] = $owner; |
| 913 | $typeArr[] = '%d'; |
| 914 | if ( isset( $data['created'] ) && ! empty( $data['created'] ) && $data['created'] !== -1 ) { |
| 915 | $dataArr['zbsli_created'] = $data['created']; |
| 916 | $typeArr[] = '%d'; |
| 917 | } else { |
| 918 | $dataArr['zbsli_created'] = time(); |
| 919 | $typeArr[] = '%d'; |
| 920 | } |
| 921 | } |
| 922 | } |
| 923 | |
| 924 | #} ========= / BUILD DATA =========== |
| 925 | |
| 926 | #} ============================================================ |
| 927 | #} ========= CHECK force_uniques & not_empty & max_len ======== |
| 928 | |
| 929 | // if we're passing limitedFields we skip these, for now |
| 930 | // #v3.1 - would make sense to unique/nonempty check just the limited fields. #gh-145 |
| 931 | if ( ! is_array( $limitedFields ) ) { |
| 932 | |
| 933 | // verify uniques |
| 934 | if ( ! $this->verifyUniqueValues( $data, $id ) ) { |
| 935 | return false; // / fails unique field verify |
| 936 | } |
| 937 | |
| 938 | // verify not_empty |
| 939 | if ( ! $this->verifyNonEmptyValues( $data ) ) { |
| 940 | return false; // / fails empty field verify |
| 941 | } |
| 942 | } |
| 943 | |
| 944 | // whatever we do we check for max_len breaches and abbreviate to avoid wpdb rejections |
| 945 | $dataArr = $this->wpdbChecks( $dataArr ); |
| 946 | |
| 947 | #} ========= / CHECK force_uniques & not_empty ================ |
| 948 | #} ============================================================ |
| 949 | |
| 950 | #} Check if ID present |
| 951 | if ( $update ) { |
| 952 | |
| 953 | #} Attempt update |
| 954 | if ( $wpdb->update( |
| 955 | $ZBSCRM_t['lineitems'], |
| 956 | $dataArr, |
| 957 | array( // where |
| 958 | 'ID' => $id, |
| 959 | ), |
| 960 | $typeArr, |
| 961 | array( // where data types |
| 962 | '%d', |
| 963 | ) |
| 964 | ) !== false ) { |
| 965 | |
| 966 | // if passing limitedFields instead of data, we ignore the following |
| 967 | // this doesn't work, because data is in args default as arr |
| 968 | // if (isset($data) && is_array($data)){ |
| 969 | // so... |
| 970 | if ( ! isset( $limitedFields ) || ! is_array( $limitedFields ) || $limitedFields == -1 ) { |
| 971 | |
| 972 | } // / if $data |
| 973 | |
| 974 | // linked to anything? |
| 975 | if ( $linkedObjType > 0 && $linkedObjID > 0 ) { |
| 976 | |
| 977 | // if not already got obj link, add it |
| 978 | $c = $this->DAL()->getObjsLinksLinkedToObj( |
| 979 | array( |
| 980 | 'objtypefrom' => ZBS_TYPE_LINEITEM, // line item type (10) |
| 981 | 'objtypeto' => $linkedObjType, // obj type (e.g. inv) |
| 982 | 'objfromid' => $id, |
| 983 | 'objtoid' => $linkedObjID, |
| 984 | 'direction' => 'both', |
| 985 | 'count' => true, |
| 986 | 'ignoreowner' => zeroBSCRM_DAL2_ignoreOwnership( ZBS_TYPE_LINEITEM ), |
| 987 | ) |
| 988 | ); |
| 989 | |
| 990 | // add link (via append) if not present |
| 991 | if ( $c <= 0 ) { |
| 992 | $this->DAL()->addUpdateObjLink( |
| 993 | array( |
| 994 | 'data' => array( |
| 995 | 'objtypefrom' => ZBS_TYPE_LINEITEM, |
| 996 | 'objtypeto' => $linkedObjType, |
| 997 | 'objfromid' => $id, |
| 998 | 'objtoid' => $linkedObjID, |
| 999 | // not req. 'owner' => $owner |
| 1000 | ), |
| 1001 | ) |
| 1002 | ); |
| 1003 | } |
| 1004 | } |
| 1005 | |
| 1006 | /* |
| 1007 | Not necessary |
| 1008 | #} INTERNAL AUTOMATOR |
| 1009 | #} & |
| 1010 | #} FALLBACKS |
| 1011 | // UPDATING CONTACT |
| 1012 | if (!$silentInsert){ |
| 1013 | |
| 1014 | // IA General lineitem update (2.87+) |
| 1015 | zeroBSCRM_FireInternalAutomator('lineitem.update',array( |
| 1016 | 'id'=>$id, |
| 1017 | 'againstid' => $id, |
| 1018 | 'data'=> $dataArr |
| 1019 | )); |
| 1020 | |
| 1021 | |
| 1022 | |
| 1023 | } */ |
| 1024 | |
| 1025 | // Successfully updated - Return id |
| 1026 | return $id; |
| 1027 | |
| 1028 | } else { |
| 1029 | |
| 1030 | $msg = __( 'DB Update Failed', 'zero-bs-crm' ); |
| 1031 | $zbs->DAL->addError( 302, $this->objectType, $msg, $dataArr ); |
| 1032 | |
| 1033 | // FAILED update |
| 1034 | return false; |
| 1035 | |
| 1036 | } |
| 1037 | } else { |
| 1038 | |
| 1039 | #} No ID - must be an INSERT |
| 1040 | if ( $wpdb->insert( |
| 1041 | $ZBSCRM_t['lineitems'], |
| 1042 | $dataArr, |
| 1043 | $typeArr |
| 1044 | ) > 0 ) { |
| 1045 | |
| 1046 | #} Successfully inserted, lets return new ID |
| 1047 | $newID = $wpdb->insert_id; |
| 1048 | |
| 1049 | // linked to anything? |
| 1050 | if ( $linkedObjType > 0 && $linkedObjID > 0 ) { |
| 1051 | |
| 1052 | // if not already got obj link, add it |
| 1053 | $c = $this->DAL()->getObjsLinksLinkedToObj( |
| 1054 | array( |
| 1055 | 'objtypefrom' => ZBS_TYPE_LINEITEM, // line item type (10) |
| 1056 | 'objtypeto' => $linkedObjType, // obj type (e.g. inv) |
| 1057 | 'objfromid' => $newID, |
| 1058 | 'objtoid' => $linkedObjID, |
| 1059 | 'direction' => 'both', |
| 1060 | 'count' => true, |
| 1061 | 'ignoreowner' => zeroBSCRM_DAL2_ignoreOwnership( ZBS_TYPE_LINEITEM ), |
| 1062 | ) |
| 1063 | ); |
| 1064 | |
| 1065 | // add link (via append) if not present |
| 1066 | if ( $c <= 0 ) { |
| 1067 | $this->DAL()->addUpdateObjLink( |
| 1068 | array( |
| 1069 | 'data' => array( |
| 1070 | 'objtypefrom' => ZBS_TYPE_LINEITEM, |
| 1071 | 'objtypeto' => $linkedObjType, |
| 1072 | 'objfromid' => $newID, |
| 1073 | 'objtoid' => $linkedObjID, |
| 1074 | // not req. 'owner' => $owner |
| 1075 | ), |
| 1076 | ) |
| 1077 | ); |
| 1078 | } |
| 1079 | } |
| 1080 | |
| 1081 | /* |
| 1082 | Not necessary |
| 1083 | #} INTERNAL AUTOMATOR |
| 1084 | #} & |
| 1085 | #} FALLBACKS |
| 1086 | // NEW CONTACT |
| 1087 | if (!$silentInsert){ |
| 1088 | |
| 1089 | #} Add to automator |
| 1090 | zeroBSCRM_FireInternalAutomator('lineitem.new',array( |
| 1091 | 'id'=>$newID, |
| 1092 | 'data'=>$dataArr, |
| 1093 | 'extsource'=>$approvedExternalSource, |
| 1094 | 'automatorpassthrough'=>$automatorPassthrough, #} This passes through any custom log titles or whatever into the Internal automator recipe. |
| 1095 | 'extraMeta'=>$confirmedExtraMeta #} This is the "extraMeta" passed (as saved) |
| 1096 | )); |
| 1097 | |
| 1098 | } |
| 1099 | */ |
| 1100 | |
| 1101 | return $newID; |
| 1102 | |
| 1103 | } else { |
| 1104 | |
| 1105 | $msg = __( 'DB Insert Failed', 'zero-bs-crm' ); |
| 1106 | $zbs->DAL->addError( 303, $this->objectType, $msg, $dataArr ); |
| 1107 | |
| 1108 | #} Failed to Insert |
| 1109 | return false; |
| 1110 | |
| 1111 | } |
| 1112 | } |
| 1113 | |
| 1114 | return false; |
| 1115 | } |
| 1116 | |
| 1117 | /** |
| 1118 | * deletes a lineitem object |
| 1119 | * |
| 1120 | * NOTE! Not to be used directly, or if so, manually delete the objlinks for this item<->obj (e.g. inv) else garbage kept in objlinks table |
| 1121 | * Use: deleteLineItemsForObject |
| 1122 | * |
| 1123 | * @param array $args Associative array of arguments |
| 1124 | * id |
| 1125 | * |
| 1126 | * @return int success; |
| 1127 | */ |
| 1128 | public function deleteLineitem( $args = array() ) { |
| 1129 | |
| 1130 | global $ZBSCRM_t, $wpdb, $zbs; |
| 1131 | |
| 1132 | #} ============ LOAD ARGS ============= |
| 1133 | $defaultArgs = array( |
| 1134 | |
| 1135 | 'id' => -1, |
| 1136 | 'saveOrphans' => true, |
| 1137 | |
| 1138 | ); |
| 1139 | foreach ( $defaultArgs as $argK => $argV ) { |
| 1140 | $$argK = $argV; |
| 1141 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 1142 | if ( is_array( $args[ $argK ] ) ) { |
| 1143 | $newData = $$argK; |
| 1144 | if ( ! is_array( $newData ) ) { |
| 1145 | $newData = array(); |
| 1146 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 1147 | $newData[ $subK ] = $subV; |
| 1148 | }$$argK = $newData; |
| 1149 | } else { |
| 1150 | $$argK = $args[ $argK ]; } |
| 1151 | } |
| 1152 | } |
| 1153 | #} =========== / LOAD ARGS ============ |
| 1154 | |
| 1155 | #} Check ID & Delete :) |
| 1156 | $id = (int) $id; |
| 1157 | if ( ! empty( $id ) && $id > 0 ) { |
| 1158 | |
| 1159 | // delete orphans? |
| 1160 | if ( $saveOrphans === false ) { |
| 1161 | |
| 1162 | } |
| 1163 | |
| 1164 | return zeroBSCRM_db2_deleteGeneric( $id, 'lineitems' ); |
| 1165 | |
| 1166 | } |
| 1167 | |
| 1168 | return false; |
| 1169 | } |
| 1170 | |
| 1171 | /** |
| 1172 | * deletes all lineitem objects assigned to another obj (quote,inv,trans) |
| 1173 | * |
| 1174 | * @param array $args Associative array of arguments |
| 1175 | * id |
| 1176 | * |
| 1177 | * @return int success; |
| 1178 | */ |
| 1179 | public function deleteLineItemsForObject( $args = array() ) { |
| 1180 | |
| 1181 | global $ZBSCRM_t, $wpdb, $zbs; |
| 1182 | |
| 1183 | #} ============ LOAD ARGS ============= |
| 1184 | $defaultArgs = array( |
| 1185 | |
| 1186 | 'objID' => -1, |
| 1187 | 'objType' => -1, |
| 1188 | |
| 1189 | ); |
| 1190 | foreach ( $defaultArgs as $argK => $argV ) { |
| 1191 | $$argK = $argV; |
| 1192 | if ( is_array( $args ) && isset( $args[ $argK ] ) ) { |
| 1193 | if ( is_array( $args[ $argK ] ) ) { |
| 1194 | $newData = $$argK; |
| 1195 | if ( ! is_array( $newData ) ) { |
| 1196 | $newData = array(); |
| 1197 | } foreach ( $args[ $argK ] as $subK => $subV ) { |
| 1198 | $newData[ $subK ] = $subV; |
| 1199 | }$$argK = $newData; |
| 1200 | } else { |
| 1201 | $$argK = $args[ $argK ]; } |
| 1202 | } |
| 1203 | } |
| 1204 | #} =========== / LOAD ARGS ============ |
| 1205 | |
| 1206 | #} Check ID & Delete :) |
| 1207 | $objID = (int) $objID; |
| 1208 | if ( ! empty( $objID ) && $objID > 0 && ! empty( $objType ) && $objType > 0 ) { |
| 1209 | |
| 1210 | $lineItems = $this->getLineitems( |
| 1211 | array( |
| 1212 | 'associatedObjType' => $objType, |
| 1213 | 'associatedObjID' => $objID, |
| 1214 | 'perPage' => 1000, |
| 1215 | 'ignoreowner' => true, |
| 1216 | ) |
| 1217 | ); |
| 1218 | |
| 1219 | $delcount = 0; |
| 1220 | if ( is_array( $lineItems ) ) { |
| 1221 | foreach ( $lineItems as $li ) { |
| 1222 | |
| 1223 | $delcount += $this->deleteLineitem( array( 'id' => $li['id'] ) ); |
| 1224 | |
| 1225 | // also delete the objlink for this |
| 1226 | $this->DAL()->deleteObjLinks( |
| 1227 | array( |
| 1228 | 'objtypefrom' => ZBS_TYPE_LINEITEM, |
| 1229 | 'objtypeto' => $objType, |
| 1230 | 'objfromid' => $li['id'], |
| 1231 | 'objtoid' => $objID, |
| 1232 | ) |
| 1233 | ); |
| 1234 | |
| 1235 | } |
| 1236 | } |
| 1237 | |
| 1238 | return $delcount; |
| 1239 | |
| 1240 | } |
| 1241 | |
| 1242 | return false; |
| 1243 | } |
| 1244 | |
| 1245 | /** |
| 1246 | * tidy's the object from wp db into clean array |
| 1247 | * |
| 1248 | * @param array $obj (DB obj) |
| 1249 | * |
| 1250 | * @return array lineitem (clean obj) |
| 1251 | */ |
| 1252 | private function tidy_lineitem( $obj = false, $withCustomFields = false ) { |
| 1253 | |
| 1254 | $res = false; |
| 1255 | |
| 1256 | if ( isset( $obj->ID ) ) { |
| 1257 | $res = array(); |
| 1258 | $res['id'] = $obj->ID; |
| 1259 | /* |
| 1260 | `zbs_site` INT NULL DEFAULT NULL, |
| 1261 | `zbs_team` INT NULL DEFAULT NULL, |
| 1262 | `zbs_owner` INT NOT NULL, |
| 1263 | */ |
| 1264 | $res['owner'] = $obj->zbs_owner; |
| 1265 | |
| 1266 | $res['order'] = (int) $obj->zbsli_order; |
| 1267 | $res['title'] = $this->stripSlashes( $obj->zbsli_title ); |
| 1268 | $res['desc'] = $this->stripSlashes( $obj->zbsli_desc ); |
| 1269 | $res['quantity'] = zeroBSCRM_format_quantity( $this->stripSlashes( $obj->zbsli_quantity ) ); |
| 1270 | $res['price'] = $this->stripSlashes( $obj->zbsli_price ); |
| 1271 | $res['currency'] = $this->stripSlashes( $obj->zbsli_currency ); |
| 1272 | $res['net'] = $this->stripSlashes( $obj->zbsli_net ); |
| 1273 | $res['discount'] = $this->stripSlashes( $obj->zbsli_discount ); |
| 1274 | $res['fee'] = $this->stripSlashes( $obj->zbsli_fee ); |
| 1275 | $res['shipping'] = $this->stripSlashes( $obj->zbsli_shipping ); |
| 1276 | $res['shipping_taxes'] = $this->stripSlashes( $obj->zbsli_shipping_taxes ); |
| 1277 | $res['shipping_tax'] = $this->stripSlashes( $obj->zbsli_shipping_tax ); |
| 1278 | $res['taxes'] = $this->stripSlashes( $obj->zbsli_taxes ); |
| 1279 | $res['tax'] = $this->stripSlashes( $obj->zbsli_tax ); |
| 1280 | $res['total'] = $this->stripSlashes( $obj->zbsli_total ); |
| 1281 | $res['created'] = (int) $obj->zbsli_created; |
| 1282 | $res['created_date'] = ( isset( $obj->zbsli_created ) && $obj->zbsli_created > 0 ) ? zeroBSCRM_locale_utsToDatetime( $obj->zbsli_created ) : false; |
| 1283 | $res['lastupdated'] = (int) $obj->zbsli_lastupdated; |
| 1284 | $res['lastupdated_date'] = ( isset( $obj->zbsli_lastupdated ) && $obj->zbsli_lastupdated > 0 ) ? zeroBSCRM_locale_utsToDatetime( $obj->zbsli_lastupdated ) : false; |
| 1285 | |
| 1286 | } |
| 1287 | |
| 1288 | return $res; |
| 1289 | } |
| 1290 | |
| 1291 | /** |
| 1292 | * Takes whatever lineitem data available and re-calculates net, total, tax etc. |
| 1293 | * .. returning same obj with updated vals |
| 1294 | * |
| 1295 | * @param array $line_item The line item. |
| 1296 | * |
| 1297 | * @return array $lineItem |
| 1298 | */ |
| 1299 | public function recalculate( $line_item = false ) { |
| 1300 | |
| 1301 | if ( is_array( $line_item ) ) { |
| 1302 | |
| 1303 | // subtotal (zbsi_net) |
| 1304 | // == line item Quantity * rate * tax% |
| 1305 | $total = 0.0; |
| 1306 | |
| 1307 | if ( isset( $line_item ) && is_array( $line_item ) ) { |
| 1308 | // Subtotal |
| 1309 | if ( isset( $line_item['quantity'] ) && isset( $line_item['price'] ) ) { |
| 1310 | |
| 1311 | $quantity = (float) $line_item['quantity']; |
| 1312 | $price = (float) $line_item['price']; |
| 1313 | |
| 1314 | // Discount? (applied to gross) |
| 1315 | // ALWAYS gross 0.00 value for lineitems (Where as at invoice level can be %) |
| 1316 | $discount = 0; |
| 1317 | if ( isset( $line_item['discount'] ) ) { |
| 1318 | $discount = (float) $line_item['discount']; |
| 1319 | } |
| 1320 | |
| 1321 | // gross |
| 1322 | $sub_total_pre_discount = $quantity * $price; |
| 1323 | |
| 1324 | $sub_total = $sub_total_pre_discount - $discount; |
| 1325 | |
| 1326 | // tax - this should be logged against line item, but lets recalc |
| 1327 | if ( isset( $line_item['taxes'] ) && $line_item['taxes'] !== -1 ) { |
| 1328 | $line_item['tax'] = zeroBSCRM_taxRates_getTaxValue( $sub_total, $line_item['taxes'] ); |
| 1329 | } |
| 1330 | |
| 1331 | // total would have discount, shipping, but as above, not using per line item as at v3.0 mvp |
| 1332 | $total = $sub_total + $line_item['tax']; |
| 1333 | } |
| 1334 | } |
| 1335 | $line_item['net'] = $sub_total_pre_discount; |
| 1336 | $line_item['total'] = $total; |
| 1337 | |
| 1338 | return $line_item; |
| 1339 | } |
| 1340 | return false; |
| 1341 | } |
| 1342 | |
| 1343 | /** |
| 1344 | * remove any non-db fields from the object |
| 1345 | * basically takes array like array('owner'=>1,'fname'=>'x','fullname'=>'x') |
| 1346 | * and returns array like array('owner'=>1,'fname'=>'x') |
| 1347 | * This does so based on the objectModel! |
| 1348 | * |
| 1349 | * @param array $obj (clean obj) |
| 1350 | * |
| 1351 | * @return array (db ready arr) |
| 1352 | */ |
| 1353 | private function db_ready_lineitem( $obj = false ) { |
| 1354 | |
| 1355 | // use the generic? (override here if necessary) |
| 1356 | return $this->db_ready_obj( $obj ); |
| 1357 | } |
| 1358 | |
| 1359 | // =========== / LINEITEM ======================================================= |
| 1360 | // =============================================================================== |
| 1361 | } // / class |