Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
tax.php
1<?php
9namespace Bitrix\Sale;
10
11
12use Bitrix\Main;
13
14Main\Localization\Loc::loadMessages(__FILE__);
15
16class Tax
17{
19 protected $order = null;
20
22 protected $list = null;
23
25 protected $availableList = null;
26
28 protected $changedValues = array();
29
31 protected $deliveryTax = null;
32
34 protected $isClone = false;
35
37 protected $isExternal = false;
38
42 protected function __construct()
43 {
44
45 }
46
50 protected static function getTaxClassName()
51 {
52 return \CSaleTax::class;
53 }
54
58 protected static function getOrderTaxClassName()
59 {
60 return \CSaleOrderTax::class;
61 }
62
66 public function getTaxList()
67 {
68 if ($this->list === null)
69 {
70 $this->list = $this->loadList();
71 }
72
73 $event = new Main\Event('sale', EventActions::EVENT_ON_TAX_GET_LIST, array(
74 'ENTITY' => $this,
75 'VALUES' => $this->list,
76 ));
77 $event->send();
78
79 if ($event->getResults())
80 {
82 foreach($event->getResults() as $eventResult)
83 {
84 if($eventResult->getType() == Main\EventResult::SUCCESS)
85 {
86 $eventResultData = $eventResult->getParameters();
87 if (!empty($eventResultData['VALUES']))
88 {
89 $this->list = $eventResultData['VALUES'];
90 }
91 }
92 }
93 }
94
95 return $this->list;
96 }
97
98
102 public function initTaxList(array $list)
103 {
104 if (!empty($list))
105 {
106 $this->list = $list;
107 $this->isExternal = true;
108 }
109 }
110
114 public function getOrder()
115 {
116 return $this->order;
117 }
118
119
126 public function calculate()
127 {
129 $result = new Result();
130
132 if (!$order = $this->getOrder())
133 {
134 throw new Main\ObjectNotFoundException('Entity "Order" not found');
135 }
136
137 $taxResult = array();
138
139 $taxList = $this->getTaxList();
140
141 $taxExempt = static::loadExemptList($order->getUserId());
142
143 $fields = array(
144 "SITE_ID" => $order->getSiteid(),
145 "PERSON_TYPE_ID" => $order->getPersonTypeId(),
146 "TAX_LOCATION" => $order->getTaxLocation(),
147 "CURRENCY" => $order->getCurrency(),
148 "USE_VAT" => $order->isUsedVat(),
149 "VAT_RATE" => $order->getVatRate(),
150 "VAT_SUM" => $order->getVatSum(),
151 );
152
153 if (is_array($taxExempt))
154 {
155 $fields['TAX_EXEMPT'] = $taxExempt;
156 }
157
158 if (is_array($taxList) && !empty($taxList))
159 {
160 $fields['TAX_LIST'] = $taxList;
161 }
162
164 $basket = $order->getBasket();
165
166 if (empty($basket))
167 return $result;
168
170 foreach ($basket as $basketItem)
171 {
172 if ($basketItem->getQuantity() == 0)
173 continue;
174 $fields['BASKET_ITEMS'][] = $basketItem->getFieldValues();
175 }
176
178 $className = static::getTaxClassName();
179 $className::calculateTax($fields, array());
180
181 if (!$order->isUsedVat() && isset($fields['TAX_LIST']) && is_array($fields['TAX_LIST']))
182 {
183 $taxResult['TAX_LIST'] = $fields['TAX_LIST'];
184 }
185
186 if (array_key_exists('TAX_PRICE', $fields) && floatval($fields['TAX_PRICE']) >= 0)
187 {
188 $taxResult['TAX_PRICE'] = $fields['TAX_PRICE'];
189 }
190
191 if (array_key_exists('VAT_SUM', $fields) && floatval($fields['VAT_SUM']) > 0)
192 {
193 $taxResult['VAT_SUM'] = $fields['VAT_SUM'];
194 }
195
196 if (array_key_exists('TAX_LIST', $fields))
197 {
198 $newTaxList = $this->checkModifyTaxList($fields['TAX_LIST']);
199 $taxResult['TAX_LIST'] = $newTaxList;
200
201 $this->list = $newTaxList;
202 }
203
204 if (!$order->isUsedVat() && empty($this->list) && is_array($this->list))
205 {
206 $taxResult['TAX_PRICE'] = 0;
207 }
208
209 if (!empty($taxResult))
210 {
211 $result->setData($taxResult);
212 }
213
214 return $result;
215 }
216
221 public function calculateDelivery()
222 {
224 $result = new Result();
225
227 if (!$order = $this->getOrder())
228 {
229 throw new Main\ObjectNotFoundException('Entity "Order" not found');
230 }
231
232 if ($order->getId() > 0 || (!empty($this->list) && is_array($this->list)))
233 {
234 $taxList = $this->getTaxList();
235 }
236 else
237 {
238 $taxList = $this->getAvailableList();
239 }
240
241 $taxExempt = static::loadExemptList($order->getUserId());
242
244 if (!$basket = $order->getBasket())
245 {
246 throw new Main\ObjectNotFoundException('Entity "Basket" not found');
247 }
248
249 $fields = array(
250 "TAX_LOCATION" => $order->getTaxLocation(),
251 "VAT_SUM" => $basket->getVatSum(),
252 "CURRENCY" => $order->getCurrency(),
253 );
254
255
256 if (!empty($taxExempt))
257 {
258 $fields['TAX_EXEMPT'] = $taxExempt;
259 }
260
261 if (!empty($taxList))
262 {
263 $fields['TAX_LIST'] = $taxList;
264 }
265
266 $options = array();
267
268 if (($isDeliveryCalculate = $this->isDeliveryCalculate()))
269 {
270 $options['COUNT_DELIVERY_TAX'] = ($isDeliveryCalculate === true ? "Y" : "N");
271 }
272
273 $shipmentCollection = $order->getShipmentCollection();
274 if (!$shipmentCollection)
275 {
276 throw new Main\ObjectNotFoundException('Entity "ShipmentCollection" not found');
277 }
278
280 foreach ($shipmentCollection as $shipment)
281 {
282 if ($shipment->isSystem())
283 continue;
284
285 $service = $shipment->getDelivery();
286 if ($service === null)
287 continue;
288
289 $additionalFields = array(
290 "DELIVERY_PRICE" => $shipment->getPrice()
291 );
292
293 $vatRate = $shipment->getVatRate();
294 if ($vatRate)
295 {
296 $additionalFields["USE_VAT"] = true;
297 $additionalFields["VAT_RATE"] = $vatRate;
298 }
299
300 $fields = array_merge($fields, $additionalFields);
301
303 $className = static::getTaxClassName();
304 $className::calculateDeliveryTax($fields, $options);
305 }
306
307
308 $taxResult = array();
309
310 if (array_key_exists('TAX_PRICE', $fields) && floatval($fields['TAX_PRICE']) > 0)
311 {
312 $taxResult['TAX_PRICE'] = $fields['TAX_PRICE'];
313 }
314
315 if (array_key_exists('VAT_SUM', $fields) && floatval($fields['VAT_SUM']) > 0)
316 {
317 $taxResult['VAT_SUM'] = $fields['VAT_SUM'];
318 }
319
320 if (array_key_exists('VAT_DELIVERY', $fields) && floatval($fields['VAT_DELIVERY']) > 0)
321 {
322 $taxResult['VAT_DELIVERY'] = $fields['VAT_DELIVERY'];
323 }
324
325
326 if ($isDeliveryCalculate && array_key_exists('TAX_LIST', $fields) && !empty($fields['TAX_LIST']) && is_array($fields['TAX_LIST']))
327 {
328 $newTaxList = $this->checkModifyTaxList($fields['TAX_LIST']);
329 $this->list = $newTaxList;
330 }
331
332 if (!empty($taxResult))
333 {
334 $result->setData($taxResult);
335 }
336
337
338 return $result;
339 }
340
341
346 protected function checkModifyTaxList(array $taxList)
347 {
348 $oldTaxList = $this->loadList();
349
350 $taxIndexList = array();
351
352 if (!empty($oldTaxList) && is_array($oldTaxList))
353 {
354 $oldTaxIndexList = array();
355 foreach ($oldTaxList as $taxOldKey => $taxOldValue)
356 {
357 $oldTaxIndexList[$taxOldValue['NAME']."|".$taxOldValue['CODE']] = $taxOldKey;
358 }
359
360 foreach ($taxList as $taxValue)
361 {
362 if (array_key_exists($taxValue['NAME']."|".$taxValue['CODE'], $oldTaxIndexList))
363 {
364 $taxIndexList[$taxValue['NAME']."|".$taxValue['CODE']] = $oldTaxIndexList[$taxValue['NAME']."|".$taxValue['CODE']];
365 }
366 }
367 }
368
369 if (!empty($taxList) && is_array($taxList))
370 {
371 foreach ($taxList as $taxKey => $taxValue)
372 {
373 $taxCode = $taxValue['NAME']."|".$taxValue['CODE'];
374 if (isset($taxIndexList[$taxCode]))
375 {
376 $oldTaxKey = $taxIndexList[$taxCode];
377
378 $oldTaxValueDat = $oldTaxList[$oldTaxKey];
379 foreach($taxValue as $key => $value)
380 {
381 if (array_key_exists($key, $oldTaxValueDat))
382 {
383 if ($oldTaxValueDat[$key] != $value)
384 {
385 $oldTaxList[$oldTaxKey][$key] = $value;
386
387 if (!in_array($taxCode, $this->changedValues))
388 {
389 $this->changedValues[$taxCode] = true;
390 }
391 }
392 }
393 else
394 {
395 $oldTaxList[$oldTaxKey][$key] = $value;
396 if (!in_array($taxCode, $this->changedValues))
397 {
398 $this->changedValues[$taxCode] = true;
399 }
400 }
401 }
402 }
403 else
404 {
405 $oldTaxList[] = array(
406 'NAME' => $taxValue['NAME'],
407 'IS_PERCENT' => $taxValue['IS_PERCENT'],
408 'VALUE' => $taxValue['VALUE'],
409 'VALUE_MONEY' => $taxValue['VALUE_MONEY'],
410 'APPLY_ORDER' => $taxValue['APPLY_ORDER'],
411 'IS_IN_PRICE' => $taxValue['IS_IN_PRICE'],
412 'TAX_VAL' => $taxValue['TAX_VAL'],
413 'CODE' => $taxValue['CODE'],
414 );
415
416 // if (!in_array($taxCode, $this->changedValues))
417 // {
418 // $this->changedValues[$taxCode] = true;
419 // }
420 }
421 }
422
423 // one tax to order
424 $taxListModify1C = array();
425 foreach($oldTaxList as $taxOrder)
426 {
427 if($taxOrder['CODE'] == 'VAT1C')
428 {
429 $taxListModify1C[] = $taxOrder;
430 }
431 }
432
433 if(count($taxListModify1C)>0)
434 {
435 $oldTaxList = $taxListModify1C;
436 }
437 }
438 else
439 {
440 $oldTaxList = array();
441 }
442
443 return $oldTaxList;
444 }
445
446
451 public function save()
452 {
453
454 $result = new Result();
456 if (!$order = $this->getOrder())
457 {
458 throw new Main\ObjectNotFoundException('Entity "Order" not found');
459 }
460
461 //DoSaveOrderTax
462
464 $className = static::getTaxClassName();
465 $className::DoSaveOrderTax($order->getId(), $this->getTaxList(), $errors);
466
467 if (!empty($errors) && is_array($errors))
468 {
469 foreach ($errors as $error)
470 {
471 $result->addError(new Main\Entity\EntityError($error));
472 }
473 }
474
475
476 if ($order->getId() > 0)
477 {
478 $registry = Registry::getInstance(static::getRegistryType());
480 $orderHistory = $registry->getOrderHistoryClassName();
481
482 $orderHistory::collectEntityFields('TAX', $order->getId());
483 }
484
485 return $result;
486 }
487
492 private static function createTaxObject()
493 {
494 $registry = Registry::getInstance(static::getRegistryType());
495 $taxClassName = $registry->getTaxClassName();
496
497 return new $taxClassName();
498 }
499
503 public static function getRegistryType()
504 {
506 }
507
513 public static function load(OrderBase $order)
514 {
515 $tax = static::createTaxObject();
516 $tax->order = $order;
517
518 if ($order->getId() > 0)
519 {
520 $tax->getTaxList();
521 }
522 else
523 {
524 $tax->list = $tax->getAvailableList();
525 }
526
527 return $tax;
528 }
529
533 protected function loadList()
534 {
535 $resultList = array();
536 $order = $this->getOrder();
537
538 if ($order->getId() <= 0)
539 return null;
540
542 $className = static::getOrderTaxClassName();
543
544 $dbTaxList = $className::GetList(
545 array("APPLY_ORDER" => "ASC"),
546 array("ORDER_ID" => $order->getId())
547 );
548 while ($taxList = $dbTaxList->Fetch())
549 {
550 $taxList['NAME'] = $taxList['TAX_NAME'];
551 $resultList[] = $taxList;
552 }
553
554 return (!empty($resultList) ? $resultList : null);
555 }
556
557
558 public function resetTaxList()
559 {
560 $this->list = array();
561 }
562
563 public function resetAvailableTaxList()
564 {
565 $this->availableList = null;
566 }
570 public function refreshData()
571 {
572 $result = new Result();
573
574 if (!$this->isExternal)
575 {
576 $this->resetTaxList();
577 }
578
579 $this->resetAvailableTaxList();
580
582 $r = $this->calculate();
583 if (!$r->isSuccess())
584 {
585 $result->addErrors($r->getErrors());
586 return $result;
587 }
588
589 $taxResult = $r->getData();
590
591 $r = $this->calculateDelivery();
592 if (!$r->isSuccess())
593 {
594 $result->addErrors($r->getErrors());
595 return $result;
596 }
597 $taxResult = array_merge($taxResult, $r->getData());
598
599 $result->setData($taxResult);
600
601 return $result;
602 }
603
608 public static function loadExemptList($userId)
609 {
610 $exemptList = array();
611
612 static $proxyUserGroups = array();
613 static $proxyTaxExemptList = array();
614
615 if (!empty($proxyUserGroups[$userId]))
616 {
617 $userGroups = $proxyUserGroups[$userId];
618 }
619 else
620 {
621 $userGroups = \CUser::GetUserGroup($userId);
622 $proxyUserGroups[$userId] = $userGroups;
623 }
624
625
626 $proxyTaxExemptKey = md5(join('|', $userGroups));
627
628 if (array_key_exists($proxyTaxExemptKey, $proxyTaxExemptList))
629 {
630 $exemptList = $proxyTaxExemptList[$proxyTaxExemptKey];
631 }
632 else
633 {
635 $className = static::getTaxClassName();
636 $dbTaxExemptList = $className::GetExemptList(array("GROUP_ID" => $userGroups));
637 while ($taxExemptList = $dbTaxExemptList->Fetch())
638 {
639 if (!in_array(intval($taxExemptList["TAX_ID"]), $exemptList))
640 $exemptList[] = intval($taxExemptList["TAX_ID"]);
641 }
642
643 $proxyTaxExemptList[$proxyTaxExemptKey] = $exemptList;
644 }
645
646
647
648 return $exemptList;
649 }
650
654 public function getAvailableList()
655 {
656 if ($this->availableList === null)
657 {
658 $this->availableList = $this->loadAvailableList();
659 }
660
661 return $this->availableList;
662
663 }
664
668 protected function loadAvailableList()
669 {
670 $order = $this->getOrder();
671 $basket = $order->getBasket();
672 if (!$basket)
673 return null;
674
675 $availableList = array();
676
677 if (!$order->isUsedVat())
678 {
679 $taxExemptList = static::loadExemptList($order->getUserId());
680
681 $taxRateRes = \CSaleTaxRate::GetList(
682 array("APPLY_ORDER" => "ASC"),
683 array(
684 "LID" => $order->getSiteId(),
685 "PERSON_TYPE_ID" => $order->getPersonTypeId(),
686 "ACTIVE" => "Y",
687 "LOCATION_CODE" => $order->getTaxLocation(),
688 )
689 );
690 while ($taxRate = $taxRateRes->GetNext())
691 {
692 if (!in_array(intval($taxRate["TAX_ID"]), $taxExemptList))
693 {
694 if ($taxRate["IS_PERCENT"] != "Y")
695 {
696 $taxRate["VALUE"] = PriceMaths::roundPrecision(\CCurrencyRates::convertCurrency($taxRate["VALUE"], $taxRate["CURRENCY"], $order->getCurrency()));
697 $taxRate["CURRENCY"] = $order->getCurrency();
698 }
699 $availableList[] = $taxRate;
700 }
701 }
702 }
703 else
704 {
705 $availableList[] = array(
706 "NAME" => Main\Localization\Loc::getMessage("SOA_VAT"),
707 "IS_PERCENT" => "Y",
708 "VALUE" => $order->getVatRate() * 100,
709 "VALUE_FORMATED" => "(".($order->getVatRate() * 100)."%, ".GetMessage("SOA_VAT_INCLUDED").")",
710 "VALUE_MONEY" => $order->getVatSum(),
711 "VALUE_MONEY_FORMATED" => SaleFormatCurrency($order->getVatSum(), $order->getCurrency()),
712 "APPLY_ORDER" => 100,
713 "IS_IN_PRICE" => "Y",
714 "CODE" => "VAT"
715 );
716 }
717
718 return $availableList;
719 }
720
724 public function setDeliveryCalculate($value)
725 {
726 $this->deliveryTax = ($value === true? true : false);
727 }
728
732 public function isDeliveryCalculate()
733 {
734 return $this->deliveryTax;
735 }
736
743 public function createClone(\SplObjectStorage $cloneEntity)
744 {
745 if ($this->isClone() && $cloneEntity->contains($this))
746 {
747 return $cloneEntity[$this];
748 }
749
750 $taxClone = clone $this;
751 $taxClone->isClone = true;
752
753 if (!$cloneEntity->contains($this))
754 {
755 $cloneEntity[$this] = $taxClone;
756 }
757
758 if ($this->order)
759 {
760 if ($cloneEntity->contains($this->order))
761 {
762 $taxClone->order = $cloneEntity[$this->order];
763 }
764 }
765
766 return $taxClone;
767 }
768
772 public function isClone()
773 {
774 return $this->isClone;
775 }
776
777}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static roundPrecision($value)
static getInstance($type)
Definition registry.php:183
static getOrderTaxClassName()
Definition tax.php:58
createClone(\SplObjectStorage $cloneEntity)
Definition tax.php:743
static getTaxClassName()
Definition tax.php:50
loadAvailableList()
Definition tax.php:668
initTaxList(array $list)
Definition tax.php:102
setDeliveryCalculate($value)
Definition tax.php:724
checkModifyTaxList(array $taxList)
Definition tax.php:346
isDeliveryCalculate()
Definition tax.php:732
static load(OrderBase $order)
Definition tax.php:513
resetAvailableTaxList()
Definition tax.php:563
static getRegistryType()
Definition tax.php:503
getAvailableList()
Definition tax.php:654