Passed
Push — master ( 01f5f1...8a4484 )
by Aimeos
04:51
created

Standard::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2024
6
 * @package MShop
7
 * @subpackage Price
8
 */
9
10
11
namespace Aimeos\MShop\Price\Item;
12
13
use \Aimeos\MShop\Common\Item\ListsRef;
0 ignored issues
show
Bug introduced by
The type \Aimeos\MShop\Common\Item\ListsRef was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use \Aimeos\MShop\Common\Item\PropertyRef;
0 ignored issues
show
Bug introduced by
The type \Aimeos\MShop\Common\Item\PropertyRef was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
17
/**
18
 * Default implementation of a price object.
19
 *
20
 * @package MShop
21
 * @subpackage Price
22
 */
23
class Standard extends Base
24
{
25
	use ListsRef\Traits, PropertyRef\Traits {
0 ignored issues
show
Bug introduced by
The type \Aimeos\MShop\Common\Item\PropertyRef\Traits was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type \Aimeos\MShop\Common\Item\ListsRef\Traits was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
		ListsRef\Traits::__clone insteadof PropertyRef\Traits;
27
		ListsRef\Traits::__clone as __cloneList;
28
		PropertyRef\Traits::__clone as __cloneProperty;
29
	}
30
31
32
	private int $precision;
33
	private ?string $tax;
34
35
36
	/**
37
	 * Initalizes the object with the given values
38
	 *
39
	 * @param string $prefix Prefix for the keys returned by toArray()
40
	 * @param array $values Associative array of key/value pairs for price, costs, rebate and currencyid
41
	 */
42
	public function __construct( string $prefix, array $values = [] )
43
	{
44
		parent::__construct( 'price.', $values );
45
46
		$this->tax = $values['price.taxvalue'] ?? null;
47
		$this->precision = (int) ( $values['.precision'] ?? 2 );
48
49
		$this->initPropertyItems( $values['.propitems'] ?? [] );
50
		$this->initListItems( $values['.listitems'] ?? [] );
51
	}
52
53
54
	/**
55
	 * Creates a deep clone of all objects
56
	 */
57
	public function __clone()
58
	{
59
		parent::__clone();
60
		$this->__cloneList();
61
		$this->__cloneProperty();
62
	}
63
64
65
   /**
66
	 * Returns costs per item.
67
	 *
68
	 * @return string Costs per item
69
	 */
70
	public function getCosts() : string
71
	{
72
		return $this->formatNumber( (float) $this->get( 'price.costs', '0.00' ) );
73
	}
74
75
76
	/**
77
	 * Sets the new costsper item.
78
	 *
79
	 * @param string|integer|double $price Amount with two digits precision
80
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
81
	 */
82
	public function setCosts( $price ) : \Aimeos\MShop\Price\Item\Iface
83
	{
84
		return $this->set( 'price.costs', $this->checkPrice( (string) $price ) );
85
	}
86
87
88
	/**
89
	 * Returns the currency ID.
90
	 *
91
	 * @return string|null Three letter ISO currency code (e.g. EUR)
92
	 */
93
	public function getCurrencyId() : ?string
94
	{
95
		return $this->get( 'price.currencyid' );
96
	}
97
98
99
	/**
100
	 * Sets the used currency ID.
101
	 *
102
	 * @param string $currencyid Three letter currency code
103
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
104
	 * @throws \Aimeos\MShop\Exception If the language ID is invalid
105
	 */
106
	public function setCurrencyId( string $currencyid ) : \Aimeos\MShop\Price\Item\Iface
107
	{
108
		return $this->set( 'price.currencyid', $this->checkCurrencyId( $currencyid, false ) );
109
	}
110
111
112
	/**
113
	 * Returns the domain the price is valid for.
114
	 *
115
	 * @return string Domain name
116
	 */
117
	public function getDomain() : string
118
	{
119
		return $this->get( 'price.domain', '' );
120
	}
121
122
123
	/**
124
	 * Sets the new domain the price is valid for.
125
	 *
126
	 * @param string $domain Domain name
127
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
128
	 */
129
	public function setDomain( string $domain ) : \Aimeos\MShop\Common\Item\Iface
130
	{
131
		return $this->set( 'price.domain', $domain );
132
	}
133
134
135
	/**
136
	 * Returns the label of the item
137
	 *
138
	 * @return string Label of the item
139
	 */
140
	public function getLabel() : string
141
	{
142
		return $this->get( 'price.label', '' );
143
	}
144
145
146
	/**
147
	 * Sets the label of the item
148
	 *
149
	 * @param string $label Label of the item
150
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
151
	 */
152
	public function setLabel( ?string $label ) : \Aimeos\MShop\Price\Item\Iface
153
	{
154
		return $this->set( 'price.label', (string) $label );
155
	}
156
157
158
	/**
159
	 * Returns the decimal precision of the price
160
	 *
161
	 * @return int Number of decimal digits
162
	 */
163
	public function getPrecision() : int
164
	{
165
		return $this->precision;
166
	}
167
168
169
	/**
170
	 * Returns the quantity the price is valid for.
171
	 *
172
	 * @return float Quantity
173
	 */
174
	public function getQuantity() : float
175
	{
176
		return (float) $this->get( 'price.quantity', 1 );
177
	}
178
179
180
	/**
181
	 * Sets the quantity the price is valid for.
182
	 *
183
	 * @param float $quantity Quantity
184
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
185
	 */
186
	public function setQuantity( float $quantity ) : \Aimeos\MShop\Price\Item\Iface
187
	{
188
		return $this->set( 'price.quantity', $quantity );
189
	}
190
191
192
	/**
193
	 * Returns the rebate amount.
194
	 *
195
	 * @return string Rebate amount
196
	 */
197
	public function getRebate() : string
198
	{
199
		return $this->formatNumber( (float) $this->get( 'price.rebate', '0.00' ) );
200
	}
201
202
203
	/**
204
	 * Sets the new rebate amount.
205
	 *
206
	 * @param string|integer|double $price Rebate amount with two digits precision
207
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
208
	 */
209
	public function setRebate( $price ) : \Aimeos\MShop\Price\Item\Iface
210
	{
211
		return $this->set( 'price.rebate', $this->checkPrice( (string) $price ) );
212
	}
213
214
215
	/**
216
	 * Returns the status of the item
217
	 *
218
	 * @return int Status of the item
219
	 */
220
	public function getStatus() : int
221
	{
222
		return $this->get( 'price.status', 1 );
223
	}
224
225
226
	/**
227
	 * Sets the status of the item
228
	 *
229
	 * @param int $status Status of the item
230
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
231
	 */
232
	public function setStatus( int $status ) : \Aimeos\MShop\Common\Item\Iface
233
	{
234
		return $this->set( 'price.status', $status );
235
	}
236
237
238
	/**
239
	 * Returns the type of the price.
240
	 *
241
	 * @return string Type of the price
242
	 */
243
	public function getType() : string
244
	{
245
		return $this->get( 'price.type', 'default' );
246
	}
247
248
249
	/**
250
	 * Sets the new type of the price.
251
	 *
252
	 * @param string $type Type of the price
253
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
254
	 */
255
	public function setType( string $type ) : \Aimeos\MShop\Common\Item\Iface
256
	{
257
		return $this->set( 'price.type', $this->checkCode( $type ) );
258
	}
259
260
261
	/**
262
	 * Returns the tax rate
263
	 *
264
	 * @return string Tax rate
265
	 */
266
	public function getTaxRate() : string
267
	{
268
		$list = (array) $this->get( 'price.taxrates', [] );
269
		return $this->formatNumber( $list['tax'] ?? '0.00' );
270
	}
271
272
273
	/**
274
	 * Returns all tax rates in percent.
275
	 *
276
	 * @return string[] Tax rates for the price
277
	 */
278
	 public function getTaxRates() : array
279
	 {
280
		return $this->get( 'price.taxrates', [] );
281
	 }
282
283
284
	/**
285
	 * Sets the new tax rate.
286
	 *
287
	 * @param string|integer|double $taxrate Tax rate with two digits precision
288
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
289
	 */
290
	public function setTaxRate( $taxrate ) : \Aimeos\MShop\Price\Item\Iface
291
	{
292
		return $this->setTaxRates( ['tax' => $taxrate] );
293
	}
294
295
296
	/**
297
	 * Sets the new tax rates in percent
298
	 *
299
	 * @param array $taxrates Tax rates with name as key and values with two digits precision
300
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
301
	 */
302
	public function setTaxRates( array $taxrates ) : \Aimeos\MShop\Price\Item\Iface
303
	{
304
		foreach( $taxrates as $name => $taxrate )
305
		{
306
			unset( $taxrates[$name] ); // change index 0 to ''
307
			$taxrates[$name ?: 'tax'] = $this->checkPrice( $taxrate );
308
		}
309
310
		return $this->set( 'price.taxrates', $taxrates );
311
	}
312
313
314
	/**
315
	 * Returns the tax rate flag.
316
	 *
317
	 * True if tax is included in the price value, costs and rebate, false if not
318
	 *
319
	 * @return bool Tax rate flag for the price
320
	 */
321
	public function getTaxFlag() : bool
322
	{
323
		return $this->get( 'price.taxflag', true );
324
	}
325
326
327
	/**
328
	 * Sets the new tax flag.
329
	 *
330
	 * @param bool $flag True if tax is included in the price value, costs and rebate, false if not
331
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
332
	 */
333
	public function setTaxFlag( bool $flag ) : \Aimeos\MShop\Price\Item\Iface
334
	{
335
		return $this->set( 'price.taxflag', $flag );
336
	}
337
338
339
	/**
340
	 * Returns the tax for the price item
341
	 *
342
	 * @return string Tax value with four digits precision
343
	 * @see mshop/price/taxflag
344
	 */
345
	public function getTaxValue() : string
346
	{
347
		if( $this->tax === null )
348
		{
349
			$taxrate = array_sum( $this->getTaxRates() );
350
351
			if( $this->getTaxFlag() !== false ) {
352
				$this->tax = ( $this->getValue() + $this->getCosts() ) / ( 100 + $taxrate ) * $taxrate;
353
			} else {
354
				$this->tax = ( $this->getValue() + $this->getCosts() ) * $taxrate / 100;
355
			}
356
357
			parent::setModified();
358
		}
359
360
		return $this->formatNumber( (float) $this->tax, $this->getPrecision() + 2 );
361
	}
362
363
364
	/**
365
	 * Sets the tax amount
366
	 *
367
	 * @param string|integer|double $value Tax value with up to four digits precision
368
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
369
	 */
370
	public function setTaxValue( $value ) : \Aimeos\MShop\Price\Item\Iface
371
	{
372
		$this->tax = $this->checkPrice( (string) $value, $this->getPrecision() + 2 );
373
		parent::setModified();
374
		return $this;
375
	}
376
377
378
	/**
379
	 * Returns the amount of money.
380
	 *
381
	 * @return string|null Price value or NULL for on request
382
	 */
383
	public function getValue() : ?string
384
	{
385
		return $this->formatNumber( $this->get( 'price.value' ) );
386
	}
387
388
389
	/**
390
	 * Sets the new amount of money.
391
	 *
392
	 * @param string|integer|double|null $price Amount with configured precision or NULL for on request
393
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
394
	 */
395
	public function setValue( $price ) : \Aimeos\MShop\Price\Item\Iface
396
	{
397
		return $this->set( 'price.value', $this->checkPrice( $price ) );
398
	}
399
400
401
	/**
402
	 * Sets the modified flag of the object.
403
	 *
404
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
405
	 */
406
	public function setModified() : \Aimeos\MShop\Common\Item\Iface
407
	{
408
		$this->tax = null;
409
		return parent::setModified();
410
	}
411
412
413
	/**
414
	 * Tests if the item is available based on status, time, language and currency
415
	 *
416
	 * @return bool True if available, false if not
417
	 */
418
	public function isAvailable() : bool
419
	{
420
		$cid = $this->get( '.currencyid' );
421
		return parent::isAvailable() && $this->getStatus() > 0 && ( $cid === null || $this->getCurrencyId() === $cid );
422
	}
423
424
425
	/**
426
	 * Add the given price to the current one.
427
	 *
428
	 * @param \Aimeos\MShop\Price\Item\Iface $item Price item which should be added
429
	 * @param float $quantity Number of times the Price should be added
430
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
431
	 */
432
	public function addItem( \Aimeos\MShop\Price\Item\Iface $item, float $quantity = 1 ) : \Aimeos\MShop\Price\Item\Iface
433
	{
434
		if( $item->getCurrencyId() != $this->getCurrencyId() )
435
		{
436
			$msg = 'Price can not be added. Currency ID "%1$s" of price item and currently used currency ID "%2$s" does not match.';
437
			throw new \Aimeos\MShop\Price\Exception( sprintf( $msg, $item->getCurrencyId(), $this->getCurrencyId() ) );
438
		}
439
440
		if( $this === $item ) { $item = clone $item; }
441
		$taxValue = $this->getTaxValue(); // use initial value before it gets reset
442
443
		$this->setQuantity( 1 );
444
		$this->setValue( $this->getValue() + $item->getValue() * $quantity );
445
		$this->setCosts( $this->getCosts() + $item->getCosts() * $quantity );
446
		$this->setRebate( $this->getRebate() + $item->getRebate() * $quantity );
447
		$this->setTaxValue( $taxValue + $item->getTaxValue() * $quantity );
448
449
		return $this;
450
	}
451
452
453
	/**
454
	 * Resets the values of the price item.
455
	 * The currency ID, domain, type and status stays the same.
456
	 *
457
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
458
	 */
459
	public function clear()
460
	{
461
		$this->setQuantity( 1 );
462
		$this->setValue( '0.00' );
463
		$this->setCosts( '0.00' );
464
		$this->setRebate( '0.00' );
465
		$this->setTaxRate( '0.00' );
466
		$this->tax = null;
467
468
		return $this;
469
	}
470
471
472
	/*
473
	 * Sets the item values from the given array and removes that entries from the list
474
	 *
475
	 * @param array &$list Associative list of item keys and their values
476
	 * @param bool True to set private properties too, false for public only
477
	 * @return \Aimeos\MShop\Price\Item\Iface Price item for chaining method calls
478
	 */
479
	public function fromArray( array &$list, bool $private = false ) : \Aimeos\MShop\Common\Item\Iface
480
	{
481
		$item = parent::fromArray( $list, $private );
482
483
		foreach( $list as $key => $value )
484
		{
485
			switch( $key )
486
			{
487
				case 'price.type': $item->setType( $value ); break;
488
				case 'price.currencyid': $item->setCurrencyId( $value ); break;
489
				case 'price.quantity': $item->setQuantity( (float) $value ); break;
490
				case 'price.domain': $item->setDomain( $value ); break;
491
				case 'price.value': $item->setValue( $value ); break;
492
				case 'price.costs': $item->setCosts( $value ); break;
493
				case 'price.rebate': $item->setRebate( $value ); break;
494
				case 'price.taxvalue': $item->setTaxValue( $value ); break;
495
				case 'price.taxrate': $item->setTaxRate( $value ); break;
496
				case 'price.taxrates': $item->setTaxRates( (array) $value ); break;
497
				case 'price.taxflag': $item->setTaxFlag( (bool) $value ); break;
498
				case 'price.status': $item->setStatus( (int) $value ); break;
499
				case 'price.label': $item->setLabel( $value ); break;
500
				default: continue 2;
501
			}
502
503
			unset( $list[$key] );
504
		}
505
506
		return $item;
507
	}
508
509
510
	/**
511
	 * Returns the item values as array.
512
	 *
513
	 * @param bool True to return private properties, false for public only
514
	 * @return array Associative list of item properties and their values
515
	 */
516
	public function toArray( bool $private = false ) : array
517
	{
518
		$list = parent::toArray( $private );
519
520
		$list['price.type'] = $this->getType();
521
		$list['price.currencyid'] = $this->getCurrencyId();
522
		$list['price.domain'] = $this->getDomain();
523
		$list['price.quantity'] = $this->getQuantity();
524
		$list['price.value'] = $this->getValue();
525
		$list['price.costs'] = $this->getCosts();
526
		$list['price.rebate'] = $this->getRebate();
527
		$list['price.taxvalue'] = $this->getTaxValue();
528
		$list['price.taxrates'] = $this->getTaxRates();
529
		$list['price.taxrate'] = $this->getTaxRate();
530
		$list['price.taxflag'] = $this->getTaxFlag();
531
		$list['price.status'] = $this->getStatus();
532
		$list['price.label'] = $this->getLabel();
533
534
		return $list;
535
	}
536
}
537