Index: branches/5.2.x/constants.php =================================================================== diff -u -N -r14258 -r14641 --- branches/5.2.x/constants.php (.../constants.php) (revision 14258) +++ branches/5.2.x/constants.php (.../constants.php) (revision 14641) @@ -1,6 +1,6 @@ getObject(); /* @var $object kDBItem */ - + $this->SetNewCode($object); - + $object->SetDBField('LastUsedBy', NULL); $object->SetDBField('LastUsedOn', NULL); $object->SetDBField('NumberOfUses', NULL); - + $expiration = $this->Application->GetVar('clone_coupon_expiration'); $object->SetDBField('Expiration_date', $expiration); $object->SetDBField('Expiration_time', $expiration); } - + function OnApplyClone(&$event) - { + { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } - + $object =& $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ @@ -106,23 +106,30 @@ $event->status = kEvent::erFAIL; return ; } - + $temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler'); /* @var $temp kTempTablesHandler */ - + $original_coupon_ids = $this->getSelectedIDs($event, true); $clone_count = $object->GetDBField('CouponCount'); - $this->Application->StoreVar('CoupLastCloneCount', $clone_count); + $this->Application->StoreVar('CoupLastCloneCount', $clone_count); $this->Application->SetVar('clone_coupon_expiration', $object->GetDBField('DefaultExpiration')); - + for ($i = 0; $i < $clone_count; $i++) { $temp->CloneItems($event->Prefix, $event->Special, $original_coupon_ids); } - $this->finalizePopup($event); + $event->SetRedirectParam('opener', 'u'); } - function setCloningRequired(&$object) + /** + * Sets fields required during coupon cloning + * + * @param kDBItem $object + * @return void + * @access protected + */ + protected function setCloningRequired(&$object) { $this->RemoveRequiredFields($object); $object->setRequired('CouponCount'); @@ -132,7 +139,7 @@ function SetNewCode(&$item) { do{ - $new_code = $this->RandomCouponCode(10); + $new_code = $this->RandomCouponCode(); $exists = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'ProductsCoupons WHERE Code='.$this->Conn->qstr($new_code)); if ($exists){ $new_code = false; @@ -142,74 +149,16 @@ $item->SetDBField('Code', $new_code); } - function RandomCouponCode($size) + function RandomCouponCode() { - $rand_code = ""; - for ($i=0; $i<10; $i++){ - $is_letter = rand(0,1); - if ($is_letter){ - $rand_char = chr(rand(65,90)); - }else{ - $rand_char = rand(0,9); - } - $rand_code .= $rand_char; - } - return $rand_code; - } + $rand_code = ''; - /** - * Enter description here... - * - * @param kEvent $event - */ - function OnApplyCoupon(&$event) - { - $code = $this->Application->GetVar('coupon_code'); - if ($code == '') { - return ; + for ($i = 0; $i < 10; $i++) { + $is_letter = rand(0, 1); + $rand_code .= ($is_letter ? chr(rand(65, 90)) : rand(0, 9)); } - $object =& $event->getObject(Array('skip_autoload' => true)); - $object->Load($code, 'Code'); - - if (!$object->isLoaded()) { - $event->status = kEvent::erFAIL; - $this->Application->SetVar('set_checkout_error', 4); - $event->redirect = false; // check!!! - return ; - } - - $expire_date = $object->GetDBField('Expiration'); - $number_of_use = $object->GetDBField('NumberOfUses'); - if( $object->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) || - (isset($number_of_use) && $number_of_use <= 0)) - { - $event->status = kEvent::erFAIL; - $this->Application->SetVar('set_checkout_error', 5); - $event->redirect->false; - return ; - } - - $last_used = adodb_mktime(); - $object->SetDBField('LastUsedBy', $this->Application->RecallVar('user_id')); - $object->SetDBField('LastUsedOn_date', $last_used); - $object->SetDBField('LastUsedOn_time', $last_used); - if(isset($number_of_use)) - { - $object->SetDBField('NumberOfUses', $number_of_use - 1); - if($number_of_use == 1) - { - $object->SetDBField('Status', 2); - } - } - $object->Update(); - - $this->Application->setUnitOption('ord', 'AutoLoad', true); - $order =& $this->Application->recallObject('ord'); - $order->SetDBField('CouponId', $object->GetDBField('CouponId')); - $order->Update(); - - $this->Application->SetVar('set_checkout_error', 10); + return $rand_code; } /** Index: branches/5.2.x/units/orders/orders_item.php =================================================================== diff -u -N -r14594 -r14641 --- branches/5.2.x/units/orders/orders_item.php (.../orders_item.php) (revision 14594) +++ branches/5.2.x/units/orders/orders_item.php (.../orders_item.php) (revision 14641) @@ -1,6 +1,6 @@ RemoveGiftCertificate($object); - $event->SetRedirectParam('checkout_error', 108); + $this->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED_AUTOMATICALLY); } + $this->SetDBField('GiftCertificateDiscount', $gift_certificate_discount); } @@ -278,4 +279,39 @@ $this->SetDBField('GiftCertificateId', 0); $this->SetDBField('GiftCertificateDiscount', 0); } + + /** + * Sets checkout error + * + * @param int $error_type = {product,coupon,gc} + * @param int $error_code + * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName} + */ + function setCheckoutError($error_type, $error_code, $product_id = null) + { + $errors = $this->Application->RecallVar('checkout_errors'); + $errors = $errors ? unserialize($errors) : Array (); + + if ( isset($product_id) ) { + $error_type .= ':' . $product_id; + + // any error takes priority over FIELD_UPDATE_SUCCESS error + if ( isset($errors[$error_type]) && $error_code == OrderCheckoutError::FIELD_UPDATE_SUCCESS ) { + return ; + } + } + + if ( is_numeric($error_code) ) { + $errors[$error_type] = $error_code; + } + else { + unset($errors[$error_type]); + } + + if ( $this->Application->isDebugMode() ) { + $this->Application->Debugger->appendHTML('CO_ERROR: ' . $error_type . ' - ' . $error_code); + } + + $this->Application->StoreVar('checkout_errors', serialize($errors)); + } } \ No newline at end of file Index: branches/5.2.x/units/order_items/order_items_event_handler.php =================================================================== diff -u -N -r14625 -r14641 --- branches/5.2.x/units/order_items/order_items_event_handler.php (.../order_items_event_handler.php) (revision 14625) +++ branches/5.2.x/units/order_items/order_items_event_handler.php (.../order_items_event_handler.php) (revision 14641) @@ -1,6 +1,6 @@ Application->recallObject('p.-item', null, array('skip_autoload' => true)); + /* @var $product_object ProductsItem */ foreach ($product_ids as $product_id) { $product_object->Load($product_id); @@ -79,44 +80,139 @@ } } - $this->finalizePopup($event); + $event->SetRedirectParam('opener', 'u'); } /** * Updates subtotal field in order record. * Only for "Items" tab in "Orders -> Order Edit" in Admin * * @param kEvent $event + * @access protected */ - function OnUpdate(&$event) + protected function OnUpdate(&$event) { - parent::OnUpdate($event); - if ( ($this->Application->GetVar('t') != 'in-commerce/orders/orders_edit_items') ) { - return true; - } + $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); - $object =& $event->getObject(); - - $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); - if (!$items_info) { - return ; + if ( !$items_info ) { + return; } - $sub_total = $this->getSubTotal($items_info); - $return_total = $this->getReturnTotal($items_info); + $object =& $event->getObject(Array ('skip_autoload' => true)); + /* @var $object kDBItem */ $table_info = $object->getLinkedInfo(); + $main_object =& $this->Application->recallObject($table_info['ParentPrefix']); + /* @var $main_object OrdersItem */ - if ($sub_total !== false) { + foreach ($items_info as $id => $field_values) { + $object->Clear(); // otherwise validation errors will be passed to next object + + $object->Load($id); + $object->SetFieldsFromHash($field_values); + $this->customProcessing($event, 'before'); + + if ( $object->Update($id) ) { + $this->customProcessing($event, 'after'); + $event->status = kEvent::erSUCCESS; + } + else { + $oi_string = $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag'); + + $field_errors = $object->GetFieldErrors(); + + foreach ($field_errors as $field => $error_params) { + $error_msg = $object->GetErrorMsg($field); + + if ( $error_msg ) { + $main_object->setCheckoutError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::FIELD_UPDATE_ERROR, $oi_string . ':' . $field); + } + } + + $event->status = kEvent::erFAIL; + $event->redirect = false; +// break; + } + } + + if ( $this->Application->GetVar('t') != 'in-commerce/orders/orders_edit_items' ) { + return; + } + + $sub_total = $this->getSubTotal($items_info); + + if ( $sub_total !== false ) { $main_object->SetDBField('SubTotal', $sub_total); } - $main_object->SetDBField('ReturnTotal', $return_total); + $main_object->SetDBField('ReturnTotal', $this->getReturnTotal($items_info)); $main_object->Update(); } /** + * Remembers what fields were changed + * + * @param kEvent $event + */ + function OnAfterItemUpdate(&$event) + { + parent::OnAfterItemUpdate($event); + + if ( $this->Application->isAdmin ) { + return ; + } + + $object =& $event->getObject(); + /* @var $object kDBItem */ + + $changed_fields = $object->GetChangedFields(); + + if ( $changed_fields ) { + $table_info = $object->getLinkedInfo(); + + $main_object =& $this->Application->recallObject( $table_info['ParentPrefix'] ); + /* @var $main_object OrdersItem */ + + $oi_string = $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag'); + + foreach ($changed_fields as $changed_field => $change_info) { + $error_code = OrderCheckoutError::FIELD_UPDATE_SUCCESS; + + if ( $changed_field == 'ItemData' ) { + $item_data_old = unserialize( $change_info['old'] ); + $item_data_new = unserialize( $change_info['new'] ); + + if ( $item_data_old['DiscountId'] != $item_data_new['DiscountId'] || $item_data_old['DiscountType'] != $item_data_new['DiscountType'] ) { + if ( $item_data_new['DiscountId'] > 0 ) { + $error_code = $item_data_new['DiscountType'] == 'discount' ? OrderCheckoutError::DISCOUNT_APPLIED : OrderCheckoutError::COUPON_APPLIED; + } + else { + $error_code = $item_data_old['DiscountType'] == 'discount' ? OrderCheckoutError::DISCOUNT_REMOVED : OrderCheckoutError::COUPON_REMOVED; + } + } + + if ( $error_code == OrderCheckoutError::DISCOUNT_APPLIED || $error_code == OrderCheckoutError::DISCOUNT_REMOVED ) { + // set general error too + $main_object->setCheckoutError(OrderCheckoutErrorType::DISCOUNT, $error_code); + } + } + elseif ( $changed_field == 'Quantity' ) { + // here is how qty is changed: + // OLD QTY -> NEW QTY + // RECALCULATE + // NEW QTY = IN_STOCK_QTY + // NEW ORDER ITEM with LEFTOVER QTY + $this->Application->Debugger->appendTrace(); + $this->Application->Debugger->appendHTML('QTY_CHANGE (' . $oi_string . '): ' . $change_info['old'] . ' => ' . $change_info['new']); + } + + $main_object->setCheckoutError(OrderCheckoutErrorType::PRODUCT, $error_code, $oi_string . ':' . $changed_field); + } + } + } + + /** * Returns subtotal * * @param Array $items_info @@ -193,7 +289,9 @@ $object =& $event->getObject(); /* @var $object kDBItem */ - if ( $item_info = $object->GetDBField('ItemData') ) { + $item_info = $object->GetDBField('ItemData'); + + if ( $item_info ) { $item_info = unserialize($item_info); $object->SetDBField('DiscountType', getArrayValue($item_info, 'DiscountType')); $object->SetDBField('DiscountId', getArrayValue($item_info, 'DiscountId')); @@ -213,13 +311,16 @@ parent::SetCustomQuery($event); $object =& $event->getObject(); + /* @var $object kDBList */ $package_num = $event->getEventParam('package_num'); - if ($package_num) $object->addFilter('package_num', 'PackageNum = '.$package_num); + if ( $package_num ) { + $object->addFilter('package_num', 'PackageNum = ' . $package_num); + } $type = $event->getEventParam('product_type'); - if ($type) { - $object->addFilter('product_type', 'p.Type ='.$type); + if ( $type ) { + $object->addFilter('product_type', 'p.Type =' . $type); } } @@ -231,19 +332,21 @@ */ function checkItemStatus(&$event) { - if ($this->Application->IsAdmin()) { + if ( $this->Application->isAdmin ) { return true; } $object =& $event->getObject(); - if (!$object->isLoaded()) { + /* @var $object kDBItem */ + + if ( !$object->isLoaded() ) { return true; } $order =& $this->Application->recallObject('ord'); /* @var $order kDBItem */ - if ($order->isLoaded() && ($order->GetID() == $object->GetDBField('OrderId'))) { + if ( $order->isLoaded() && ($order->GetID() == $object->GetDBField('OrderId')) ) { return $order->GetDBField('PortalUserId') == $this->Application->RecallVar('user_id'); } Index: branches/5.2.x/units/orders/orders_event_handler.php =================================================================== diff -u -N -r14618 -r14641 --- branches/5.2.x/units/orders/orders_event_handler.php (.../orders_event_handler.php) (revision 14618) +++ branches/5.2.x/units/orders/orders_event_handler.php (.../orders_event_handler.php) (revision 14641) @@ -1,6 +1,6 @@ Array('self' => true), 'OnRemoveFromCart' => Array('self' => true), 'OnUpdateCart' => Array('self' => true), + 'OnUpdateCartJSON' => Array('self' => true), 'OnUpdateItemOptions' => Array('self' => true), 'OnCleanupCart' => Array('self' => true), 'OnContinueShopping' => Array('self' => true), @@ -120,6 +121,7 @@ 'OnProceedToBilling' => Array('self' => true), 'OnProceedToPreview' => Array('self' => true), 'OnCompleteOrder' => Array('self' => true), + 'OnCombinedPlaceOrder' => Array('self' => true), 'OnRemoveCoupon' => Array('self' => true), 'OnRemoveGiftCertificate' => Array('self' => true), @@ -161,6 +163,8 @@ function OnQuietPreSave(&$event) { $object =& $event->getObject(); + /* @var $object kDBItem */ + $object->IgnoreValidation = true; $event->CallSubEvent('OnPreSave'); $object->IgnoreValidation = false; @@ -178,6 +182,7 @@ } $object =& $event->getObject(); + /* @var $object kDBItem */ $shipping_address_id = $this->Application->GetVar('shipping_address_id'); $billing_address_id = $this->Application->GetVar('billing_address_id'); @@ -294,6 +299,7 @@ $this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] ); $gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] ); + /* @var $gateway_object kGWBase */ $payment_result = $gateway_object->DirectPayment($order->GetFieldValues(), $gw_data['gw_params']); $sql = 'UPDATE %s SET GWResult1 = %s WHERE %s = %s'; @@ -319,6 +325,8 @@ function PrepareCoupons(&$event, &$order) { $order_items =& $this->Application->recallObject('orditems.-inv','orditems_List',Array('skip_counting'=>true,'per_page'=>-1) ); + /* @var $order_items kDBList */ + $order_items->linkToParent($order->Special); $order_items->Query(); $order_items->GoFirst(); @@ -512,15 +520,11 @@ function OnCheckout(&$event) { $this->OnUpdateCart($event); - if ($event->getEventParam('RecalculateChangedCart')) - { - $event->SetRedirectParam('checkout_error', $event->getRedirectParam('checkout_error')); - } - else - { + if ( !$event->getEventParam('RecalculateChangedCart') ) { $object =& $event->getObject(); - if(!$object->HasTangibleItems()) - { + /* @var $object OrdersItem */ + + if ( !$object->HasTangibleItems() ) { $object->SetDBField('ShippingTo', ''); $object->SetDBField('ShippingCompany', ''); $object->SetDBField('ShippingPhone', ''); @@ -545,7 +549,10 @@ $event->redirect = $this->Application->GetVar('next_step_template'); $order_id = $this->Application->GetVar('order_id'); - if($order_id !== false) $event->SetRedirectParam('ord_id', $order_id); + + if ( $order_id !== false ) { + $event->SetRedirectParam('ord_id', $order_id); + } } } @@ -596,10 +603,16 @@ */ function OnAfterItemUpdate(&$event) { + parent::OnAfterItemUpdate($event); + $object =& $event->getObject(); + /* @var $object OrdersItem */ $cvv2 = $object->GetDBField('PaymentCVV2'); - if($cvv2 !== false) $this->Application->StoreVar('CVV2Code', $cvv2); + + if ( $cvv2 !== false ) { + $this->Application->StoreVar('CVV2Code', $cvv2); + } } @@ -684,6 +697,57 @@ } /** + * Updates cart and returns various info in JSON format + * + * @param kEvent $event + */ + function OnUpdateCartJSON(&$event) + { + if ( $this->Application->GetVar('ajax') != 'yes' ) { + return; + } + + $object =& $event->getObject(); + /* @var $object kDBItem */ + + // 1. delete given order item by id + $delete_id = $this->Application->GetVar('delete_id'); + + if ( $delete_id !== false ) { + $sql = 'DELETE FROM ' . TABLE_PREFIX . 'OrderItems + WHERE OrderId = ' . $object->GetID() . ' AND OrderItemId = ' . (int)$delete_id; + $this->Conn->Query($sql); + } + + // 2. remove coupon + $remove = $this->Application->GetVar('remove'); + + if ( $remove == 'coupon' ) { + $this->RemoveCoupon($object); + $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED); + } + elseif ( $remove == 'gift_certificate' ) { + $this->RemoveGiftCertificate($object); + $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED); + } + + // 3. update product quantities and recalculate all discounts + $this->Application->HandleEvent($items_event, 'orditems:OnUpdate'); + $event->CallSubEvent('OnRecalculateItems'); + + // 4. remove "orditems" object of kDBItem class, since getOrderInfo uses kDBList object under same prefix + $this->Application->removeObject('orditems'); + + $order_helper =& $this->Application->recallObject('OrderHelper'); + /* @var $order_helper OrderHelper */ + + $event->status = kEvent::erSTOP; + $currency = $this->Application->GetVar('currency', 'selected'); + + echo json_encode( $order_helper->getOrderInfo($object, $currency) ); + } + + /** * Adds item to cart * * @param kEvent $event @@ -946,30 +1010,114 @@ return $item_data; } + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnApplyCoupon(&$event) + { + $code = $this->Application->GetVar('coupon_code'); + + if ($code == '') { + return ; + } + + $object =& $event->getObject(); + /* @var $object OrdersItem */ + + $coupon =& $this->Application->recallObject('coup', null, Array ('skip_autoload' => true)); + /* @var $coupon kDBItem */ + + $coupon->Load($code, 'Code'); + + if ( !$coupon->isLoaded() ) { + $event->status = kEvent::erFAIL; + $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_CODE_INVALID); + $event->redirect = false; // check!!! + + return ; + } + + $expire_date = $coupon->GetDBField('Expiration'); + $number_of_use = $coupon->GetDBField('NumberOfUses'); + if ( $coupon->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) || + (isset($number_of_use) && $number_of_use <= 0)) + { + $event->status = kEvent::erFAIL; + $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_CODE_EXPIRED); + $event->redirect = false; + + return ; + } + + $last_used = adodb_mktime(); + $coupon->SetDBField('LastUsedBy', $this->Application->RecallVar('user_id')); + $coupon->SetDBField('LastUsedOn_date', $last_used); + $coupon->SetDBField('LastUsedOn_time', $last_used); + + + if ( isset($number_of_use) ) { + $coupon->SetDBField('NumberOfUses', $number_of_use - 1); + + if ($number_of_use == 1) { + $coupon->SetDBField('Status', 2); + } + } + + $coupon->Update(); + + $this->Application->setUnitOption('ord', 'AutoLoad', true); + $order =& $this->Application->recallObject('ord'); + /* @var $order OrdersItem */ + + $order->SetDBField('CouponId', $coupon->GetDBField('CouponId')); + $order->SetDBField('CouponName', $coupon->GetDBField('Name')); // calculated field + + $order->Update(); + + $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_APPLIED); +// OnApplyCoupon is called as hook for OnUpdateCart/OnCheckout, which calls OnRecalcualate themself + } + + /** + * Removes coupon from order + * + * @param kEvent $event + * @deprecated + */ function OnRemoveCoupon(&$event) { $object =& $event->getObject(); + /* @var $object OrdersItem */ + $this->RemoveCoupon($object); + $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED); + $event->CallSubEvent('OnRecalculateItems'); - $event->SetRedirectParam('checkout_error', 7); } + /** + * Removes coupon from a given order + * + * @param OrdersItem $object + */ function RemoveCoupon(&$object) { - $coupon_id = $object->GetDBField('CouponId'); $coupon =& $this->Application->recallObject('coup', null, Array('skip_autoload' => true)); - $res = $coupon->Load($coupon_id); - $uses = $coupon->GetDBField('NumberOfUses'); + /* @var $coupon kDBItem */ - if($res && isset($uses)) - { - $coupon->SetDBField('NumberOfUses', $uses + 1); - $coupon->SetDBField('Status', 1); + $coupon->Load( $object->GetDBField('CouponId') ); + + if ( $coupon->isLoaded() ) { + $coupon->SetDBField('NumberOfUses', $coupon->GetDBField('NumberOfUses') + 1); + $coupon->SetDBField('Status', STATUS_ACTIVE); $coupon->Update(); } + $object->SetDBField('CouponId', 0); + $object->SetDBField('CouponName', ''); // calculated field $object->SetDBField('CouponDiscount', 0); - } /** @@ -1436,7 +1584,7 @@ } } - $this->finalizePopup($event); + $event->SetRedirectParam('opener', 'u'); } function OnMassPlaceOrder(&$event) @@ -2193,9 +2341,9 @@ */ function CheckQuantites(&$event) { - if ($this->OnRecalculateItems($event)) { // if something has changed in the order - if ($this->Application->isAdminUser) { - if ($this->UseTempTables($event)) { + if ( $this->OnRecalculateItems($event) ) { // if something has changed in the order + if ( $this->Application->isAdminUser ) { + if ( $this->UseTempTables($event) ) { $event->redirect = 'in-commerce/orders/orders_edit_items'; } } @@ -2365,18 +2513,20 @@ $ord_id = $order->GetId(); $shipping_option = $order->GetDBField('ShippingOption'); - $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag'; + $backorder_select = $shipping_option == 0 ? '0' : 'oi.BackOrderFlag'; // setting PackageNum to 0 for Non-tangible items, for tangibles first package num is always 1 - $query = ' SELECT OrderItemId - FROM '.$table_prefix.'OrderItems - LEFT JOIN '.TABLE_PREFIX.'Products - ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId - WHERE '.TABLE_PREFIX.'Products.Type > 1 AND OrderId = '.$ord_id; + $query = ' SELECT oi.OrderItemId + FROM ' . $table_prefix . 'OrderItems oi + LEFT JOIN ' . TABLE_PREFIX . 'Products p ON p.ProductId = oi.ProductId + WHERE p.Type > 1 AND oi.OrderId = ' . $ord_id; $non_tangibles = $this->Conn->GetCol($query); + if ($non_tangibles) { - $query = 'UPDATE '.$table_prefix.'OrderItems SET PackageNum = 0 WHERE OrderItemId IN ('.implode(',', $non_tangibles).')'; + $query = ' UPDATE ' . $table_prefix . 'OrderItems + SET PackageNum = 0 + WHERE OrderItemId IN (' . implode(',', $non_tangibles) . ')'; $this->Conn->Query($query); } @@ -2386,7 +2536,7 @@ // 2 => ProductId // 3 => Shipping PackageNum $query = 'SELECT - '.$backorder_select.', + '.$backorder_select.' AS BackOrderFlagCalc, PackageNum, ProductName, ShippingTypeId, @@ -2407,8 +2557,8 @@ LEFT JOIN '.TABLE_PREFIX.'Products ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId WHERE OrderId = '.$ord_id.' - GROUP BY BackOrderFlag, Grouping - ORDER BY BackOrderFlag ASC, PackageNum ASC, ProductType ASC'; + GROUP BY BackOrderFlagCalc, Grouping + ORDER BY BackOrderFlagCalc ASC, PackageNum ASC, ProductType ASC'; $sub_orders = $this->Conn->Query($query); @@ -2474,7 +2624,7 @@ $original_amount = $sub_order->GetDBField('SubTotal') + $sub_order->GetDBField('ShippingCost') + $sub_order->GetDBField('VAT') + $sub_order->GetDBField('ProcessingFee') + $sub_order->GetDBField('InsuranceFee') - $sub_order->GetDBField('GiftCertificateDiscount'); $sub_order->SetDBField('OriginalAmount', $original_amount); - if ($named_grouping_data['Type'] == 1 && ($sub_order_data['BackOrderFlag'] > 0 + if ($named_grouping_data['Type'] == 1 && ($sub_order_data['BackOrderFlagCalc'] > 0 || ($sub_order_data['TotalItems'] != $sub_order_data['TotalReserved'])) ) { $sub_order->SetDBField('Status', ORDER_STATUS_BACKORDERS); @@ -2500,10 +2650,10 @@ break; case PRODUCT_TYPE_TANGIBLE: - $sql = 'SELECT '.$backorder_select.', oi.* + $sql = 'SELECT '.$backorder_select.' AS BackOrderFlagCalc, oi.* FROM '.TABLE_PREFIX.'OrderItems oi LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId - WHERE (OrderId = %s) AND (BackOrderFlag = 0) AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')'; + WHERE (OrderId = %s) AND (BackOrderFlagCalc = 0) AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')'; $products = $this->Conn->Query( sprintf($sql, $ord_id) ); foreach ($products as $product) { @@ -2648,10 +2798,6 @@ $manager->addProduct($product, $event->getEventParam('ItemData'), $qty, $package_num); $this->Application->HandleEvent($ord_event, 'ord:OnRecalculateItems'); - - /*if ($ord_event->getEventParam('RecalculateChangedCart') && !$this->Application->isAdmin) { - $event->SetRedirectParam('checkout_error', $ord_event->getRedirectParam('checkout_error')); - }*/ } /** @@ -2661,30 +2807,25 @@ */ function UpdateShippingTotal(&$event) { - if ($this->Application->GetVar('ebay_notification') == 1) { + if ( $this->Application->GetVar('ebay_notification') == 1 ) { // TODO: get rid of this "if" - return ; + return; } + $object =& $event->getObject(); - $ord_id = $object->GetId(); + /* @var $object OrdersItem */ - $shipping_option = $object->GetDBField('ShippingOption'); - $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag'; + $shipping_total = $insurance_fee = 0; + $shipping_info = $object->GetDBField('ShippingInfo') ? unserialize($object->GetDBField('ShippingInfo')) : false; - $table_prefix = $this->TablePrefix($event); - - $shipping_info = $object->GetDBField('ShippingInfo') ? unserialize( $object->GetDBField('ShippingInfo') ) : false; - $shipping_total = 0; - $insurance_fee = 0; - if( is_array($shipping_info) ) - { - foreach ($shipping_info as $a_shipping) - { - // $id_elements = explode('_', $a_shipping['ShippingTypeId']); + if ( is_array($shipping_info) ) { + foreach ($shipping_info as $a_shipping) { +// $id_elements = explode('_', $a_shipping['ShippingTypeId']); $shipping_total += $a_shipping['TotalCost']; $insurance_fee += $a_shipping['InsuranceFee']; } } + $object->SetDBField('ShippingCost', $shipping_total); $object->SetDBField('InsuranceFee', $insurance_fee); // no need to update, it will be called in calling method @@ -2693,7 +2834,7 @@ } /** - * Recompile shopping cart, splitting or grouping orders and backorders depending on total quantityes. + * Recompile shopping cart, splitting or grouping orders and backorders depending on total quantities. * First it counts total qty for each ProductId, and then creates order for available items * and backorder for others. It also updates the sub-total for the order * @@ -2707,11 +2848,6 @@ return ; } - if($checkout_error = $this->Application->GetVar('set_checkout_error')) - { - $event->SetRedirectParam('checkout_error', $checkout_error); - } - $order =& $event->getObject(); /* @var $order OrdersItem */ @@ -2734,16 +2870,12 @@ $manager->setOrder($order); $result = $manager->calculate(); - if ( $manager->getError() ) { - $event->SetRedirectParam('checkout_error', $manager->getError()); - } - - if ($order->GetDBField('CouponId') && $order->GetDBField('CouponDiscount') == 0) { + if ( $order->GetDBField('CouponId') && $order->GetDBField('CouponDiscount') == 0 ) { $this->RemoveCoupon($order); - $event->SetRedirectParam('checkout_error', 8); + $order->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED_AUTOMATICALLY); } - if ($result) { + if ( $result ) { $this->UpdateShippingOption($event); } @@ -2753,26 +2885,25 @@ $this->RecalculateTax($event); $this->RecalculateGift($event); - if ($event->Name != 'OnAfterItemUpdate') { + if ( $event->Name != 'OnAfterItemUpdate' ) { $order->Update(); } $event->setEventParam('RecalculateChangedCart', $result); - if (is_object($event->MasterEvent)) { + if ( is_object($event->MasterEvent) ) { $event->MasterEvent->setEventParam('RecalculateChangedCart', $result); } - if ($result && ($event->getEventParam('checkout_error') === false)) { - $event->SetRedirectParam('checkout_error', 1); - } + /*if ( $result && !getArrayValue($event->redirect_params, 'checkout_error') ) { + $event->SetRedirectParam('checkout_error', OrderCheckoutError::STATE_CHANGED); + }*/ - if ($result && is_object($event->MasterEvent) && $event->MasterEvent->Name == 'OnUserLogin') - { - if( ($shop_cart_template = $this->Application->GetVar('shop_cart_template')) - && is_object($event->MasterEvent->MasterEvent) ) - { - $event->MasterEvent->MasterEvent->SetRedirectParam('checkout_error', 9); + if ( $result && is_object($event->MasterEvent) && $event->MasterEvent->Name == 'OnUserLogin' ) { + $shop_cart_template = $this->Application->GetVar('shop_cart_template'); + + if ( $shop_cart_template && is_object($event->MasterEvent->MasterEvent) ) { +// $event->MasterEvent->MasterEvent->SetRedirectParam('checkout_error', OrderCheckoutError::CHANGED_AFTER_LOGIN); $event->MasterEvent->MasterEvent->redirect = $shop_cart_template; } } @@ -3412,12 +3543,67 @@ } // ===== Gift Certificates Related ===== + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnApplyGiftCertificate(&$event) + { + $code = $this->Application->GetVar('giftcert_code'); + + if ( $code == '' ) { + return; + } + + $object =& $event->getObject(); + /* @var $object OrdersItem */ + + $gift_certificate =& $this->Application->recallObject('gc', null, Array ('skip_autoload' => true)); + /* @var $gift_certificate kDBItem */ + + $gift_certificate->Load($code, 'Code'); + + if ( !$gift_certificate->isLoaded() ) { + $event->status = kEvent::erFAIL; + $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_CODE_INVALID); + $event->redirect = false; // check!!! + + return; + } + + $debit = $gift_certificate->GetDBField('Debit'); + $expire_date = $gift_certificate->GetDBField('Expiration'); + + if ( $gift_certificate->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) || ($debit <= 0) ) { + $event->status = kEvent::erFAIL; + $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_CODE_EXPIRED); + $event->redirect = false; + + return; + } + + $object->SetDBField('GiftCertificateId', $gift_certificate->GetDBField('GiftCertificateId')); + $object->Update(); + + $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_APPLIED); + } + + /** + * Removes gift certificate from order + * + * @param kEvent $event + * @deprecated + */ function OnRemoveGiftCertificate(&$event) { $object =& $event->getObject(); + /* @var $object OrdersItem */ + $this->RemoveGiftCertificate($object); + $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED); + $event->CallSubEvent('OnRecalculateItems'); - $event->SetRedirectParam('checkout_error', 107); } function RemoveGiftCertificate(&$object) @@ -3460,6 +3646,8 @@ ini_set('max_execution_time', '0'); $object =& $event->getObject(); + /* @var $object kDBItem */ + $file = $object->GetDBField('ShippingTracking') . '.pdf'; $full_path = USPS_LABEL_FOLDER . $file; @@ -3471,4 +3659,15 @@ header('Content-Disposition: attachment; filename="' . $file . '"'); readfile($full_path); } + + /** + * Enter description here... + * + * @param kEvent $event + */ + function OnCombinedPlaceOrder(&$event) + { + + $event->CallSubEvent('OnUpdate'); + } } \ No newline at end of file Index: branches/5.2.x/units/helpers/helpers_config.php =================================================================== diff -u -N --- branches/5.2.x/units/helpers/helpers_config.php (revision 0) +++ branches/5.2.x/units/helpers/helpers_config.php (revision 14641) @@ -0,0 +1,14 @@ + 'in-commerce-helpers', + + 'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'), + + 'RegisterClasses' => Array ( + Array ('pseudo' => 'OrderHelper', 'class' => 'OrderHelper', 'file' => 'order_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'), + ), + ); Index: branches/5.2.x/units/helpers/order_helper.php =================================================================== diff -u -N --- branches/5.2.x/units/helpers/order_helper.php (revision 0) +++ branches/5.2.x/units/helpers/order_helper.php (revision 14641) @@ -0,0 +1,142 @@ +Application->RecallVar('checkout_errors'); + + $ret = Array ( + 'order' => Array ( + 'CouponId' => (int)$object->GetDBField('CouponId'), + 'CouponName' => (string)$object->GetDBField('CouponName'), + 'GiftCertificateId' => (int)$object->GetDBField('GiftCertificateDiscount'), + 'GiftCertificateDiscount' => $this->convertCurrency($object->GetDBField('GiftCertificateDiscount'), $currency), + 'DiscountTotal' => $this->convertCurrency($object->GetDBField('DiscountTotal'), $currency), + 'SubTotal' => $this->convertCurrency($object->GetDBField('SubTotal'), $currency), + ), + 'items' => Array (), + 'errors' => $errors ? unserialize($errors) : Array (), + ); + + $items =& $this->Application->recallObject('orditems', 'orditems_List', Array ('per_page' => -1)); + /* @var $items kDBList */ + + $items->Query(); + $items->GoFirst(); + + if ( $items->EOL() ) { + return $ret; + } + + $product =& $this->Application->recallObject('p', null, Array ('skip_autoload' => true)); + /* @var $product kCatDBItem */ + + $sql = $product->GetSelectSQL() . ' + WHERE ' . $product->TableName . '.' . $product->IDField . ' IN (' . implode(',', $items->GetCol('ProductId')) . ')'; + $products = $this->Conn->Query($sql, $product->IDField); + + while ( !$items->EOL() ) { + // prepare product from order item + $product->LoadFromHash( $products[ $items->GetDBField('ProductId') ] ); + $this->Application->SetVar('orditems_id', $items->GetID()); // for edit/delete links using GET + + // weird code from orditems:PrintList + $this->Application->SetVar('p_id', $product->GetID()); + $this->Application->SetVar('m_cat_id', $product->GetDBField('CategoryId')); + + // collect order item info + $url_params = Array ( + 'p_id' => $product->GetID(), + 'm_cat_id' => $product->GetDBField('CategoryId'), + 'pass' => 'm,p', + ); + + $product_url = $this->Application->HREF('__default__', '', $url_params); + + $item_data = $items->GetDBField('ItemData'); + $item_data = $item_data ? unserialize($item_data) : Array (); + + $row_index = $items->GetDBField('ProductId') . ':' . $items->GetDBField('OptionsSalt') . ':' . $items->GetDBField('BackOrderFlag'); + + $ret['items'][$row_index] = Array ( + 'product_url' => $product_url, + 'product_type' => $product->GetDBField('Type'), + 'options' => isset($item_data['Options']) ? $item_data['Options'] : false, + 'free_promo_shipping' => $this->eligibleForFreePromoShipping($items), + + 'fields' => Array ( + 'OrderItemId' => $items->GetDBField('OrderItemId'), + 'ProductName' => $items->GetDBField('ProductName'), + 'BackOrderFlag' => (int)$items->GetDBField('BackOrderFlag'), + 'FlatPrice' => $this->convertCurrency($items->GetDBField('FlatPrice'), $currency), + 'Price' => $this->convertCurrency($items->GetDBField('Price'), $currency), + 'Quantity' => (int)$items->GetDBField('Quantity'), + 'Virtual' => (int)$items->GetDBField('Virtual'), + 'Type' => (int)$product->GetDBField('Type'), + + 'cust_Availability' => $product->GetDBField('cust_Availability'), + ), + ); + + $items->GoNext(); + } + + if ( $remove_errors ) { + $this->Application->RemoveVar('checkout_errors'); + } + + return $ret; + } + + function convertCurrency($amount, $currency) + { + $converter =& $this->Application->recallObject('kCurrencyRates'); + /* @var $converter kCurrencyRates */ + + // convert primary currency to selected (if they are the same, converter will just return) + return (float)$converter->Convert($amount, 'PRIMARY', $this->getISO($currency)); + } + + function getISO($currency) + { + if ($currency == 'selected') { + $iso = $this->Application->RecallVar('curr_iso'); + } + elseif ($currency == 'primary' || $currency == '') { + $iso = $this->Application->GetPrimaryCurrency(); + } + else { //explicit currency + $iso = strtoupper($currency); + } + + return $iso; + } + + /** + * Checks, that given order item is eligible for free promo shipping + * + * @param kDBItem|kDBList $order_item + * @return bool + */ + function eligibleForFreePromoShipping(&$order_item) + { + if ( $order_item->GetDBField('Type') != PRODUCT_TYPE_TANGIBLE ) { + return false; + } + + $free_shipping = $order_item->GetDBField('MinQtyFreePromoShipping'); + + return $free_shipping > 0 && $free_shipping <= $order_item->GetDBField('Quantity'); + } + } Index: branches/5.2.x/units/order_items/order_items_tag_processor.php =================================================================== diff -u -N -r14569 -r14641 --- branches/5.2.x/units/order_items/order_items_tag_processor.php (.../order_items_tag_processor.php) (revision 14569) +++ branches/5.2.x/units/order_items/order_items_tag_processor.php (.../order_items_tag_processor.php) (revision 14641) @@ -1,6 +1,6 @@ Application->recallObject('ord'); - if ($order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE) { + /* @var $order kDBList */ + + if ( $order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE ) { $params['grid'] = $params['NotEditable']; } else { @@ -29,24 +30,27 @@ return $this->Application->ProcessParsedTag('m', 'ParseBlock', $params); } - function IsTangible($params){ - $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); - if ($object->GetDBField('Type') == 1) - return true; - else - return false; + function IsTangible($params) + { + $object =& $this->getObject($params); + /* @var $object kDBItem */ + + return $object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE; } function HasQty($params) { - $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); - $type = $object->GetDBField('Type'); - return in_array($type, array(1,6)); + $object =& $this->getObject($params); + /* @var $object kDBItem */ + + return in_array($object->GetDBField('Type'), Array (PRODUCT_TYPE_TANGIBLE, 6)); } function HasDiscount($params) { - $object =& $this->Application->recallObject( $this->getPrefixSpecial() ); + $object =& $this->getObject($params); + /* @var $object kDBItem */ + return (float)$object->GetDBField('ItemDiscount') ? 1 : 0; } @@ -60,31 +64,34 @@ function PrintOptions($params) { $object =& $this->getObject($params); + /* @var $object kDBItem */ + $item_data = @unserialize($object->GetDBField('ItemData')); $render_as = $this->SelectParam($params, 'render_as'); $block_params['name'] = $render_as; $opt_helper =& $this->Application->recallObject('kProductOptionsHelper'); + /* @var $opt_helper kProductOptionsHelper */ $o = ''; $options = $item_data['Options']; foreach ($options as $opt => $val) { - if (!is_array($val)) { + if ( !is_array($val) ) { $val = kUtil::unhtmlentities($val); } $key_data = $opt_helper->ConvertKey($opt, $object->GetDBField('ProductId')); $parsed = $opt_helper->ExplodeOptionValues($key_data); - if ($parsed) { + if ( $parsed ) { $values = $parsed['Values']; $prices = $parsed['Prices']; $price_types = $parsed['PriceTypes']; } else { - $values = array(); - $prices = array(); - $price_types = array(); + $values = array (); + $prices = array (); + $price_types = array (); } $key = $key_data['Name']; @@ -93,10 +100,11 @@ }*/ $lang =& $this->Application->recallObject('lang.current'); + /* @var $lang LanguagesItem */ - if ($render_as) { + if ( $render_as ) { $block_params['option'] = $key; - if (is_array($val)) { + if ( is_array($val) ) { $block_params['value'] = $val; $block_params['type'] = $key_data['OptionType']; $block_params['price'] = $prices; @@ -106,9 +114,9 @@ $price_type = array_key_exists($val, $price_types) ? $price_types[$val] : ''; $price = array_key_exists($val, $prices) ? $prices[$val] : ''; - if ($price_type == '$') { + if ( $price_type == '$' ) { $iso = $this->GetISO($params['currency']); - $value = $this->AddCurrencySymbol($lang->formatNumber($this->ConvertCurrency($price_type, $iso),2), $iso, true); // true to force sign + $value = $this->AddCurrencySymbol($lang->formatNumber($this->ConvertCurrency($price_type, $iso), 2), $iso, true); // true to force sign $block_params['price'] = $value; $block_params['price_type'] = ''; $block_params['sign'] = ''; // sign is included in the formatted value @@ -121,10 +129,10 @@ $block_params['value'] = htmlspecialchars($val); $block_params['type'] = $key_data['OptionType']; } - $o.= $this->Application->ParseBlock($block_params, 1); + $o .= $this->Application->ParseBlock($block_params, 1); } else { - $o .= $key.': '.$val.'
'; + $o .= $key . ': ' . $val . '
'; } } return $o; @@ -158,6 +166,8 @@ $block_params['name'] = $params['render_as']; $values = $this->Application->Parser->GetParam('value'); + /* @var $values Array */ + $prices = $this->Application->Parser->GetParam('price'); $price_types = $this->Application->Parser->GetParam('price_type'); @@ -224,7 +234,7 @@ $this->Application->SetVar('m_cat_id', $product_object->GetDBField('CategoryId')); - $block_params['is_last'] = ($i == $list->SelectedCount - 1); + $block_params['is_last'] = ($i == $list->GetSelectedCount() - 1); $o.= $this->Application->ParseBlock($block_params, 1); $list->GoNext(); @@ -242,17 +252,46 @@ function DisplayOptionsPricing($params) { $object =& $this->getObject($params); - if ($object->GetDBField('OptionsSelectionMode') == 1) { + /* @var $object kDBItem */ + + if ( $object->GetDBField('OptionsSelectionMode') == 1 ) { return false; } $item_data = unserialize($object->GetDBField('ItemData')); - if (!is_array($item_data)) return false; + if ( !is_array($item_data) ) { + return false; + } + $options = getArrayValue($item_data, 'Options'); $helper =& $this->Application->recallObject('kProductOptionsHelper'); + /* @var $helper kProductOptionsHelper */ + $crc = $helper->OptionsSalt($options, true); - $combs = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'ProductOptionCombinations WHERE CombinationCRC = '.$crc.' AND ProductId = '.$object->GetDBField('ProductId').' AND (Price != 0 OR (PriceType = 1 AND Price = 0))'); - return $combs == 0; // no overriding combinations found + $sql = 'SELECT COUNT(*) + FROM ' . TABLE_PREFIX . 'ProductOptionCombinations + WHERE CombinationCRC = ' . $crc . ' AND ProductId = ' . $object->GetDBField('ProductId') . ' AND (Price != 0 OR (PriceType = 1 AND Price = 0))'; + + return $this->Conn->GetOne($sql) == 0; // no overriding combinations found } + + function RowIndex($params) + { + $object =& $this->getObject($params); + /* @var $object kDBItem */ + + return $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag'); + } + + function FreePromoShippingAvailable($params) + { + $object =& $this->getObject($params); + /* @var $object kDBItem */ + + $order_helper =& $this->Application->recallObject('OrderHelper'); + /* @var $order_helper OrderHelper */ + + return $order_helper->eligibleForFreePromoShipping($object); + } } \ No newline at end of file Index: branches/5.2.x/units/orders/order_calculator.php =================================================================== diff -u -N -r14436 -r14641 --- branches/5.2.x/units/orders/order_calculator.php (.../order_calculator.php) (revision 14436) +++ branches/5.2.x/units/orders/order_calculator.php (.../order_calculator.php) (revision 14641) @@ -1,6 +1,6 @@ manager->setError($error_code); + $this->manager->setError($error_type, $error_code, $product_id); } /** @@ -228,7 +232,12 @@ if ($to_order < $item['Quantity']) { // ordered less, then requested -> inform user - $this->setError($to_order > 0 ? 2 : 3); + if ( $to_order > 0 ) { + $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_UNAVAILABLE, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity'); + } + else { + $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_OUT_OF_STOCK, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity'); + } } } } @@ -364,7 +373,8 @@ if ($qty > 0 && $qty < $min_qty) { // qty in cart increased to meat minimal qry requirements of given product - $this->setError(6); + $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_CHANGED_TO_MINIMAL, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity'); + $item['Quantity'] = $min_qty; } } Index: branches/5.2.x/units/orders/orders_tag_processor.php =================================================================== diff -u -N -r14258 -r14641 --- branches/5.2.x/units/orders/orders_tag_processor.php (.../orders_tag_processor.php) (revision 14258) +++ branches/5.2.x/units/orders/orders_tag_processor.php (.../orders_tag_processor.php) (revision 14641) @@ -1,6 +1,6 @@ -1)); - $o_items = $this->Application->ProcessParsedTag(rtrim('orditems.'.$this->Special, '.'), 'PrintList', $tag_params); + $o_items = $this->Application->ProcessParsedTag(rtrim('orditems.' . $this->Special, '.'), 'PrintList', $tag_params); - if ($o_items) { - $cart_params = array('name' => $params['header_render_as']); - $o = $this->Application->ParseBlock($cart_params); + if ( $o_items ) { + if ( isset($params['header_render_as']) ) { + $cart_params = array ('name' => $params['header_render_as']); + $o .= $this->Application->ParseBlock($cart_params); + } + $o .= $o_items; - $cart_params = array('name' => $params['footer_render_as']); - $o .= $this->Application->ParseBlock($cart_params); - } else { - $cart_params = array('name' => $params['empty_cart_render_as']); + + if ( isset($params['footer_render_as']) ) { + $cart_params = array ('name' => $params['footer_render_as']); + $o .= $this->Application->ParseBlock($cart_params); + } + } + elseif ( isset($params['empty_cart_render_as']) ) { + $cart_params = array ('name' => $params['empty_cart_render_as']); $o = $this->Application->ParseBlock($cart_params); } @@ -203,7 +210,7 @@ return $this->CartNotEmpty($params) ? false : true; } - function CartHasBackorders($params) + function CartHasBackorders($params = Array ()) { $object =& $this->getObject($params); @@ -219,63 +226,82 @@ function PrintShippings($params) { $o = ''; + $limitations_cache = Array (); + $object =& $this->getObject($params); - $ord_id = $object->GetId(); + /* @var $object kDBItem */ - $shipping_option = $object->GetDBField('ShippingOption'); - $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag'; + $ord_id = $object->GetID(); + $oi_table = $this->Application->getUnitOption('orditems', 'TableName'); - $order_items =& $this->Application->recallObject('orditems', 'orditems_List', Array('skip_autoload' => true) ); - $oi_table = $order_items->TableName; + if ( $object->IsTempTable() ) { + $oi_table = $this->Application->GetTempName($oi_table, 'prefix' . $object->Prefix); + } - list($split_shipments, $limit_types) = $this->GetShippingLimitations($ord_id); - foreach ($split_shipments as $group => $data) - { - $query = 'UPDATE '.$oi_table.' SET SplitShippingGroup = '.$group.' - WHERE ProductId IN ('.implode(',', $data['Products']).')'; - $this->Conn->Query($query); + list ($split_shipments, $limit_types) = $this->GetShippingLimitations($ord_id); + + foreach ($split_shipments as $group => $data) { + $sql = 'UPDATE ' . $oi_table . ' + SET SplitShippingGroup = ' . $group . ' + WHERE ProductId IN (' . implode(',', $data['Products']) . ')'; + $this->Conn->Query($sql); + $limitations_cache[$group] = $data['Types']; } + $shipping_group_option = $object->GetDBField('ShippingGroupOption'); - $shipping_group_select = $shipping_group_option == 0 ? '0 AS SplitShippingGroup' : 'SplitShippingGroup'; - if (count($split_shipments) > 1) { - $this->Application->SetVar('shipping_limitations_apply', 1); + $shipping_group_select = $shipping_group_option == ORDER_GROUP_SHIPPMENTS_AUTO ? '0' : 'oi.SplitShippingGroup'; + + if ( count($split_shipments) > 1 ) { // different shipping limitations apply - if ($limit_types == 'NONE') { + $this->Application->SetVar('shipping_limitations_apply', 1); + + if ( $limit_types == 'NONE' ) { // order can't be shipped with single shipping type + $shipping_group_option = ORDER_GROUP_SHIPPMENTS_MANUAL; + $shipping_group_select = 'oi.SplitShippingGroup'; $this->Application->SetVar('shipping_limitations_apply', 2); - $shipping_group_select = 'SplitShippingGroup'; - $shipping_group_option = 1; } } else { $this->Application->SetVar('shipping_limitations_apply', 0); } + + + + $shipping_option = $object->GetDBField('ShippingOption'); + $weight_sql = 'IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity)'; - $query = 'SELECT - '.$backorder_select.', - oi.ProductName, - oi.ShippingTypeId, - SUM(oi.Quantity) AS TotalItems, - SUM('.$weight_sql.') AS TotalWeight, - SUM(oi.Price * oi.Quantity) AS TotalAmount,'. - // calculate free Totals => SUM(ALL) - SUM(PROMO) ' - 'SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo, - SUM('.$weight_sql.') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, '.$weight_sql.', 0)) AS TotalWeightPromo, - SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo, - '.$shipping_group_select.' - FROM '.$oi_table.' oi - LEFT JOIN '.$this->Application->getUnitOption('p', 'TableName').' p - ON oi.ProductId = p.ProductId - WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1 - GROUP BY BackOrderFlag, SplitShippingGroup - ORDER BY BackOrderFlag ASC, SplitShippingGroup ASC'; - $shipments = $this->Conn->Query($query); + $sql = 'SELECT + ' . ($shipping_option == ORDER_SHIP_ALL_TOGETHER ? '0' : 'oi.BackOrderFlag') . ' AS BackOrderFlagCalc, + oi.ProductName, + oi.ShippingTypeId, - $block_params = Array(); + SUM(oi.Quantity) AS TotalItems, + SUM(' . $weight_sql . ') AS TotalWeight, + SUM(oi.Price * oi.Quantity) AS TotalAmount, + + SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo, + SUM(' . $weight_sql . ') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, ' . $weight_sql . ', 0)) AS TotalWeightPromo, + SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo, + + ' . $shipping_group_select . ' AS SplitShippingGroupCalc + FROM ' . $oi_table . ' oi + LEFT JOIN ' . TABLE_PREFIX . 'Products p ON oi.ProductId = p.ProductId + WHERE oi.OrderId = ' . $ord_id . ' AND p.Type = ' . PRODUCT_TYPE_TANGIBLE . ' + GROUP BY BackOrderFlagCalc, SplitShippingGroupCalc + ORDER BY BackOrderFlagCalc ASC, SplitShippingGroupCalc ASC'; + $shipments = $this->Conn->Query($sql); + + + + + + + $block_params = Array (); $block_params['name'] = $this->SelectParam($params, 'render_as,block'); $block_params['user_country_id'] = $object->GetDBField('ShippingCountry'); $block_params['user_state_id'] = $object->GetDBField('ShippingState'); @@ -285,54 +311,51 @@ $block_params['user_addr2'] = $object->GetDBField('ShippingAddress2'); $block_params['user_name'] = $object->GetDBField('ShippingTo'); - if( ($block_params['user_addr1'] == '' || $block_params['user_city'] == '' || - $block_params['user_zip'] == '' || $block_params['user_country_id'] == '') && - getArrayValue($params, 'invalid_address_render_as')) - { - $block_params['name'] = $params['invalid_address_render_as']; - return $this->Application->ParseBlock($block_params); - } - $group = 1; foreach ($shipments as $shipment) { - $where = array('OrderId = '.$ord_id); - if ($shipping_group_option != 0) { - $where[] = 'SplitShippingGroup = '.$shipment['SplitShippingGroup']; + $where = Array ('OrderId = ' . $ord_id); + + if ( $shipping_group_option == ORDER_GROUP_SHIPPMENTS_MANUAL ) { + $where[] = 'SplitShippingGroup = ' . $shipment['SplitShippingGroupCalc']; } - if ($shipping_option > 0) { // not all together - $where[] = 'BackOrderFlag = '.$shipment['BackOrderFlag']; + + if ( $shipping_option != ORDER_SHIP_ALL_TOGETHER ) { + $where[] = 'BackOrderFlag = ' . $shipment['BackOrderFlagCalc']; } - $query = 'UPDATE '.$oi_table.' SET PackageNum = '.$group.' - '.($where ? 'WHERE '.implode(' AND ', $where) : ''); - $this->Conn->Query($query); + $sql = 'UPDATE ' . $oi_table . ' + SET PackageNum = ' . $group . ' + WHERE ' . implode(' AND ', $where); + $this->Conn->Query($sql); + $group++; } - $this->Application->RemoveVar('LastShippings'); + $group = 1; + $this->Application->RemoveVar('LastShippings'); $this->Application->SetVar('ShipmentsExists', 1); + foreach ($shipments as $shipment) { + $block_params['package_num'] = $group; + $block_params['limit_types'] = $shipping_group_option == ORDER_GROUP_SHIPPMENTS_AUTO ? $limit_types : $limitations_cache[ $shipment['SplitShippingGroupCalc'] ]; + $this->Application->SetVar('ItemShipmentsExists', 1); // also set from Order_PrintShippingTypes tag - $block_params['package_num'] = $group; - - $block_params['limit_types'] = strpos($shipping_group_select, '0 AS') !== false ? $limit_types : $limitations_cache[$shipment['SplitShippingGroup']]; - - $this->Application->SetVar('ItemShipmentsExists', 1); - - switch ($shipment['BackOrderFlag']) { + switch ( $shipment['BackOrderFlagCalc'] ) { case 0: - if ( $this->CartHasBackOrders(Array()) && $shipping_option == 0 ) { + if ( $this->CartHasBackOrders() && $shipping_option == ORDER_SHIP_ALL_TOGETHER ) { $block_params['shipment'] = $this->Application->Phrase('lu_all_available_backordered'); } else { $block_params['shipment'] = $this->Application->Phrase('lu_ship_all_available');; } break; + case 1: $block_params['shipment'] = $this->Application->Phrase('lu_ship_all_backordered');; break; + default: $block_params['shipment'] = $this->Application->Phrase('lu_ship_backordered'); break; @@ -345,133 +368,127 @@ $block_params['weight_metric'] = $shipment['TotalWeight']; $block_params['weight'] = $shipment['TotalWeight']; - $regional =& $this->Application->recallObject('lang.current'); - if ($block_params['weight_metric'] == '') - { + if ( $block_params['weight_metric'] == '' ) { $block_params['weight'] = $this->Application->Phrase('lu_NotAvailable'); } - elseif ($regional->GetDBField('UnitSystem') == 1) - { - $block_params['weight'] .= ' '.$this->Application->Phrase('lu_kg'); + else { + $block_params['weight'] = $this->_formatWeight( $block_params['weight'] ); } - elseif ($regional->GetDBField('UnitSystem') == 2) - { - list($pounds, $ounces) = kUtil::Kg2Pounds($block_params['weight']); - $block_params['weight'] = $pounds.' '.$this->Application->Phrase('lu_pounds').' '. - $ounces.' '.$this->Application->Phrase('lu_ounces'); - } + $block_params['items'] = $shipment['TotalItems']; - $iso = $this->GetISO($params['currency']); - $amount = $this->ConvertCurrency($shipment['TotalAmount'], $iso); + $amount = $this->ConvertCurrency($shipment['TotalAmount'], $this->GetISO( $params['currency'] )); $amount = sprintf("%.2f", $amount); -// $block_params['amount'] = $this->AddCurrencySymbol($amount, $iso); $block_params['amount'] = $shipment['TotalAmount']; - $block_params['field_name'] = $this->InputName(Array('field' => 'ShippingTypeId')).'['.($group).']'; + $block_params['field_name'] = $this->InputName( Array('field' => 'ShippingTypeId') ) . '[' . $group . ']'; $parsed_block = $this->Application->ParseBlock($block_params); - if($this->Application->GetVar('ItemShipmentsExists')) - { + if ( $this->Application->GetVar('ItemShipmentsExists') ) { $o .= $parsed_block; } - else - { + else { $this->Application->SetVar('ShipmentsExists', 0); - if(getArrayValue($params, 'no_shipments_render_as')) - { + + if ( getArrayValue($params, 'no_shipments_render_as') ) { $block_params['name'] = $params['no_shipments_render_as']; + return $this->Application->ParseBlock($block_params); } } + $group++; } - if(getArrayValue($params, 'table_header_render_as')) - { - $o = $this->Application->ParseBlock( Array('name' => $params['table_header_render_as']) ).$o; - } - if(getArrayValue($params, 'table_footer_render_as')) - { - $o .= $this->Application->ParseBlock( Array('name' => $params['table_footer_render_as']) ); - } - return $o; } - function GetShippingLimitations($ord_id) + /** + * Checks, that all given address fields are valid + * + * @param Array $params + * @return bool + */ + function AddressValid($params) { - /*$query = 'SELECT - c.CachedShippingLimitation - FROM '.TABLE_PREFIX.'OrderItems AS oi - LEFT JOIN '.TABLE_PREFIX.'Products AS p - ON p.ProductId = oi.ProductId - LEFT JOIN '.TABLE_PREFIX.'CategoryItems AS ci - ON ci.ItemResourceId = p.ResourceId - LEFT JOIN '.TABLE_PREFIX.'Category AS c - ON c.CategoryId = ci.CategoryId - WHERE - oi.OrderId = '.$ord_id.' - AND - ci.PrimaryCat = 1 - AND - c.CachedShippingMode = 1;'; - $cat_limitations = $this->Conn->GetCol($query);*/ - $cat_limitations = array(); + $object =& $this->getObject($params); + /* @var $object kDBItem */ - $query = 'SELECT ShippingLimitation, ShippingMode, oi.ProductId as ProductId - FROM '.TABLE_PREFIX.'Products AS p - LEFT JOIN '.TABLE_PREFIX.'OrderItems AS oi ON - oi.ProductId = p.ProductId - WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1'; // .' AND p.ShippingMode = 1'; - $limitations = $this->Conn->Query($query, 'ProductId'); + $address_type = isset($params['type']) ? strtolower($params['type']) : 'shipping'; + $address_type = ucfirst($address_type); - $split_shipments = array(); + $check_fields = Array ('Address1', 'City', 'Zip', 'Country'); + foreach ($check_fields as $check_field) { + if ( $object->GetDBField($address_type . $check_field) == '' ) { + return false; + } + } + + return true; + } + + function GetShippingLimitations($ord_id) + { $limit = false; + $types_index = $split_shipments = $cat_limitations = Array (); - $types_index = array(); + $sql = 'SELECT p.ShippingLimitation, p.ShippingMode, oi.ProductId AS ProductId + FROM ' . TABLE_PREFIX . 'Products p + LEFT JOIN ' . TABLE_PREFIX . 'OrderItems AS oi ON oi.ProductId = p.ProductId + WHERE oi.OrderId = ' . $ord_id . ' AND p.Type = ' . PRODUCT_TYPE_TANGIBLE; + $limitations = $this->Conn->Query($sql, 'ProductId'); - // group products by shipping type range and caculate intersection of all types available for ALL products - // the intersaction caclulation is needed to determine if the order can be shipped with single type or not + // group products by shipping type range and calculate intersection of all types available for ALL products + // the intersection calculation is needed to determine if the order can be shipped with single type or not + if ($limitations) { - $limit_types = null; - foreach ($limitations as $product_id => $row) - { + $limit_types = false; + + foreach ($limitations as $product_id => $row) { // if shipping types are limited - get the types - $types = $row['ShippingLimitation'] != '' ? explode('|', substr($row['ShippingLimitation'], 1, -1)) : array('ANY'); + $types = $row['ShippingLimitation'] != '' ? explode('|', substr($row['ShippingLimitation'], 1, -1)) : Array ('ANY'); + // if shipping is NOT limited to selected types (default - so products with no limitations at all also counts) - if ($row['ShippingMode'] == 0) { + if ($row['ShippingMode'] == PRODUCT_SHIPPING_MODE_ANY_AND_SELECTED) { array_push($types, 'ANY'); // can be shipped with ANY (literally) type $types = array_unique($types); } + //adding product id to split_shipments group by types range $i = array_search(serialize($types), $types_index); if ($i === false) { $types_index[] = serialize($types); - $i = count($types_index)-1; + $i = count($types_index) - 1; } + $split_shipments[$i]['Products'][] = $product_id; $split_shipments[$i]['Types'] = serialize($types); - if ($limit_types == null) { //it is null only when we process first item with limitations - $limit_types = $types; //initial scope + + if ($limit_types === false) { + // it is false only when we process first item with limitations + $limit_types = $types; // initial scope } // this is to avoid ANY intersect CUST_1 = (), but allows ANY intersect CUST_1,ANY = (ANY) - if (in_array('ANY', $limit_types) && !in_array('ANY', $types)) { + if ( in_array('ANY', $limit_types) && !in_array('ANY', $types) ) { array_splice($limit_types, array_search('ANY', $limit_types), 1, $types); } + // this is to avoid CUST_1 intersect ANY = (), but allows CUST_1 intersect CUST_1,ANY = (ANY) - if (!in_array('ANY', $limit_types) && in_array('ANY', $types)) { + if ( !in_array('ANY', $limit_types) && in_array('ANY', $types) ) { array_splice($types, array_search('ANY', $types), 1, $limit_types); } + $limit_types = array_intersect($limit_types, $types); } + $limit_types = count($limit_types) > 0 ? serialize(array_unique($limit_types)) : 'NONE'; } - return array($split_shipments, $limit_types); + + return Array ($split_shipments, $limit_types); } function PaymentTypeForm($params) @@ -915,33 +932,132 @@ function CartHasError($params) { - return $this->Application->GetVar('checkout_error') > 0; + return $this->Application->RecallVar('checkout_errors'); } function CheckoutError($params) { - $error_codes = Array ( - 1 => 'state_changed', - 2 => 'qty_unavailable', - 3 => 'outofstock', - 4 => 'invalid_code', - 5 => 'code_expired', - 6 => 'min_qty', - 7 => 'code_removed', - 8 => 'code_removed_automatically', - 9 => 'changed_after_login', - 10 => 'coupon_applied', - 104 => 'invalid_gc_code', - 105 => 'gc_code_expired', - 107 => 'gc_code_removed', - 108 => 'gc_code_removed_automatically', - 110 => 'gift_certificate_applied', - ); + $errors = $this->Application->RecallVar('checkout_errors'); - $error_param = $error_codes[ $this->Application->GetVar('checkout_error') ]; - return $this->Application->Phrase($params[$error_param]); + if ( !$errors ) { + return ''; + } + +// $this->Application->RemoveVar('checkout_errors'); + $errors = unserialize($errors); + + if ( isset($errors[OrderCheckoutErrorType::COUPON]) ) { + $mapping = Array ( + OrderCheckoutError::COUPON_APPLIED => 'coupon_applied', + OrderCheckoutError::COUPON_REMOVED => 'code_removed', + OrderCheckoutError::COUPON_REMOVED_AUTOMATICALLY => 'code_removed_automatically', + OrderCheckoutError::COUPON_CODE_INVALID => 'invalid_code', + OrderCheckoutError::COUPON_CODE_EXPIRED => 'code_expired', + ); + + $error_phrase = $mapping[ $errors[OrderCheckoutErrorType::COUPON] ]; + } + elseif ( isset($errors[OrderCheckoutErrorType::GIFT_CERTIFICATE]) ) { + $mapping = Array ( + OrderCheckoutError::GC_APPLIED => 'gift_certificate_applied', + OrderCheckoutError::GC_REMOVED => 'gc_code_removed', + OrderCheckoutError::GC_REMOVED_AUTOMATICALLY => 'gc_code_removed_automatically', + OrderCheckoutError::GC_CODE_INVALID => 'invalid_gc_code', + OrderCheckoutError::GC_CODE_EXPIRED => 'gc_code_expired', + + ); + + $error_phrase = $mapping[ $errors[OrderCheckoutErrorType::GIFT_CERTIFICATE] ]; + } + else { + $mapping = Array ( + OrderCheckoutError::QTY_UNAVAILABLE => 'qty_unavailable', + OrderCheckoutError::QTY_OUT_OF_STOCK => 'outofstock', + OrderCheckoutError::QTY_CHANGED_TO_MINIMAL => 'min_qty', + ); + + foreach ($errors as $error_type => $error_code) { + if ( isset($mapping[$error_code]) ) { + $error_phrase = $mapping[$error_code]; + break; + } + } + + if ( !isset($error_phrase) ) { + $error_phrase = 'state_changed'; // 'changed_after_login' + } + } + + return $this->Application->Phrase( $params[$error_phrase] ); } + /** + * Returns checkout errors in JSON format + * + * @param Array $params + * @return string + */ + function PrintOrderInfo($params) + { + $order_helper =& $this->Application->recallObject('OrderHelper'); + /* @var $order_helper OrderHelper */ + + $object =& $this->getObject($params); + /* @var $object kDBItem */ + + $currency = isset($params['currency']) ? $params['currency'] : 'selected'; + + return json_encode( $order_helper->getOrderInfo($object, $currency) ); + } + + /** + * Returns currency mark (%s is $amount placemark) + * + * @param Array $params + * @return string + */ + function CurrencyMask($params) + { + $iso = $this->GetISO( $params['currency'] ); + + return $this->AddCurrencySymbol('%s', $iso); + } + + function CheckoutErrorNew($params) + { + $errors = $this->Application->RecallVar('checkout_errors'); + + if ( !$errors ) { + return ''; + } + +// $this->Application->RemoveVar('checkout_errors'); + $errors = unserialize($errors); + + $reflection = new ReflectionClass('OrderCheckoutErrorType'); + $error_types = $reflection->getConstants(); + + $reflection = new ReflectionClass('OrderCheckoutError'); + $error_codes = $reflection->getConstants(); + + $ret = Array (); + + foreach ($errors as $error_type => $error_code) { + $error_type = explode(':', $error_type); + + $error_explained = '' . array_search($error_type[0], $error_types) . ''; + + if ( $error_type[0] == OrderCheckoutErrorType::PRODUCT ) { + $error_explained .= ' (ProductId = ' . $error_type[1] . '; OptionsSalt: ' . $error_type[2] . '; BackOrderFlag: ' . $error_type[3] . '; Field: ' . $error_type[4] . ')'; + } + + $error_explained .= ' - ' . array_search($error_code, $error_codes) . ''; + $ret[] = $error_explained; + } + + return implode('
', $ret); + } + function GetFormAction($params) { $object =& $this->getObject($params); @@ -1311,14 +1427,6 @@ return $max <= 0 ? true : $address_list->GetRecordsCount() < $max; } - function FreePromoShippingAvailable($params) - { - $object =& $this->Application->recallObject('orditems'); - $free_ship = $object->GetDBField('MinQtyFreePromoShipping'); - $tangible = ($object->GetDBField('Type') == 1)? 1 : 0; - return ($tangible && ($free_ship > 0 && $free_ship <= $object->GetDBField('Quantity')))? 1 : 0; - } - /** * Creates link for removing coupon or gift certificate * @@ -1358,22 +1466,29 @@ return $this->Application->Phrase('lu_NotAvailable'); } + return $this->_formatWeight($total_weight); + } + + function _formatWeight($weight) + { $regional =& $this->Application->recallObject('lang.current'); + /* @var $regional kDBItem */ - switch ($regional->GetDBField('UnitSystem')) { + switch ( $regional->GetDBField('UnitSystem') ) { case 1: // metric system -> add kg sign - $total_weight .= ' '.$this->Application->Phrase('lu_kg'); + $weight .= ' ' . $this->Application->Phrase('lu_kg'); break; case 2: // uk system -> convert to pounds - list($pounds, $ounces) = kUtil::Kg2Pounds($total_weight); - $total_weight = $pounds.' '.$this->Application->Phrase('lu_pounds').' '.$ounces.' '.$this->Application->Phrase('lu_ounces'); + list ($pounds, $ounces) = kUtil::Kg2Pounds($weight); + + $weight = $pounds . ' ' . $this->Application->Phrase('lu_pounds') . ' ' . $ounces . ' ' . $this->Application->Phrase('lu_ounces'); break; } - return $total_weight; + return $weight; } function InitCatalogTab($params) @@ -1494,5 +1609,4 @@ return $o; } - } \ No newline at end of file Index: branches/5.2.x/units/order_items/order_items_config.php =================================================================== diff -u -N -r14582 -r14641 --- branches/5.2.x/units/order_items/order_items_config.php (.../order_items_config.php) (revision 14582) +++ branches/5.2.x/units/order_items/order_items_config.php (.../order_items_config.php) (revision 14641) @@ -1,6 +1,6 @@ Array('type'=>'double','formatter'=>'kFormatter','format'=>'%01.2f','default'=>'0.00'), 'SKU' => Array('type' => 'string', 'default' => ''), 'MinQtyFreeShipping'=> Array('type' => 'int', 'default' => 0,), + 'Virtual' => Array ('type' => 'int', 'default' => 0), ), 'Grids' => Array ( Index: branches/5.2.x/units/orders/orders_config.php =================================================================== diff -u -N -r14594 -r14641 --- branches/5.2.x/units/orders/orders_config.php (.../orders_config.php) (revision 14594) +++ branches/5.2.x/units/orders/orders_config.php (.../orders_config.php) (revision 14641) @@ -1,6 +1,6 @@ 'OnRecalculateItems', ), - /* OnApplyCoupon is called as hook for OnUpdateCart/OnCheckout, which calls OnRecalcualate themself - Array ( - 'Mode' => hAFTER, + Array( + 'Mode' => hBEFORE, 'Conditional' => false, - 'HookToPrefix' => 'coup', + 'HookToPrefix' => '', 'HookToSpecial' => '', - 'HookToEvent' => Array ( 'OnApplyCoupon' ), + 'HookToEvent' => Array( 'OnUpdateCart', 'OnUpdateCartJSON', 'OnCheckout' ), 'DoPrefix' => '', 'DoSpecial' => '', - 'DoEvent' => 'OnRecalculateItems', - ),*/ + 'DoEvent' => 'OnApplyCoupon', + ), + Array ( + 'Mode' => hBEFORE, + 'Conditional' => false, + 'HookToPrefix' => '', + 'HookToSpecial' => '', + 'HookToEvent' => Array( 'OnUpdateCart', 'OnUpdateCartJSON', 'OnCheckout' ), + 'DoPrefix' => '', + 'DoSpecial' => '', + 'DoEvent' => 'OnApplyGiftCertificate', + ), + Array ( 'Mode' => hAFTER, 'Conditional' => false, Index: branches/5.2.x/units/orders/order_manager.php =================================================================== diff -u -N -r14258 -r14641 --- branches/5.2.x/units/orders/order_manager.php (.../order_manager.php) (revision 14258) +++ branches/5.2.x/units/orders/order_manager.php (.../order_manager.php) (revision 14641) @@ -1,6 +1,6 @@ 'state_changed', 2 => 'qty_unavailable', @@ -96,7 +89,6 @@ function reset() { - $this->errorCode = 0; $this->operations = Array (); $this->totalsOverride = Array (); @@ -109,23 +101,34 @@ } /** - * Sets error from last operation + * Sets checkout error * + * @param int $error_type = {product,coupon,gc} * @param int $error_code + * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName} + * @return void + * @access public */ - public function setError($error_code) + public function setError($error_type, $error_code, $product_id = null) { - $this->errorCode = $error_code; + $this->order->setCheckoutError($error_type, $error_code, $product_id); } /** - * Sets error from last operation + * Gets error count * * @return int + * @access public */ - public function getError() + public function getErrorCount() { - return $this->errorCode; + $errors = $this->Application->RecallVar('checkout_errors'); + + if ( !$errors ) { + return 0; + } + + return count( unserialize($errors) ); } /** @@ -146,7 +149,7 @@ { $this->calculator->calculate(); - $changed = $this->applyOperations() || ($this->getError() > 0); + $changed = $this->applyOperations() || ($this->getErrorCount() > 0); $this->setOrderTotals(); return $changed; Index: branches/5.2.x/units/coupons/coupons_config.php =================================================================== diff -u -N -r14582 -r14641 --- branches/5.2.x/units/coupons/coupons_config.php (.../coupons_config.php) (revision 14582) +++ branches/5.2.x/units/coupons/coupons_config.php (.../coupons_config.php) (revision 14641) @@ -1,6 +1,6 @@ Array('class'=>'CouponsEventHandler','file'=>'coupons_event_handler.php','build_event'=>'OnBuild'), 'TagProcessorClass' => Array('class'=>'CouponsTagProcessor','file'=>'coupons_tag_processor.php','build_event'=>'OnBuild'), 'AutoLoad' => true, - 'Hooks' => Array( - Array( - 'Mode' => hBEFORE, - 'Conditional' => false, - 'HookToPrefix' => 'ord', - 'HookToSpecial' => '', - 'HookToEvent' => Array( 'OnUpdateCart', 'OnCheckout' ), - 'DoPrefix' => '', - 'DoSpecial' => '', - 'DoEvent' => 'OnApplyCoupon', - ), - ), + 'QueryString' => Array( 1 => 'id', 2 => 'Page', Index: branches/5.2.x/units/orders/order_validator.php =================================================================== diff -u -N -r14594 -r14641 --- branches/5.2.x/units/orders/order_validator.php (.../order_validator.php) (revision 14594) +++ branches/5.2.x/units/orders/order_validator.php (.../order_validator.php) (revision 14641) @@ -1,6 +1,6 @@ dataSource->GetDBField($cardtype_field) ) { case 2: // MasterCard Index: branches/5.2.x/units/gift_certificates/gift_certificates_config.php =================================================================== diff -u -N -r14582 -r14641 --- branches/5.2.x/units/gift_certificates/gift_certificates_config.php (.../gift_certificates_config.php) (revision 14582) +++ branches/5.2.x/units/gift_certificates/gift_certificates_config.php (.../gift_certificates_config.php) (revision 14641) @@ -1,6 +1,6 @@ Array ('class' => 'GiftCertificateTagProcessor', 'file' => 'gift_certificates_tp.php', 'build_event' => 'OnBuild'), 'AutoLoad' => true, - 'Hooks' => Array ( - Array ( - 'Mode' => hBEFORE, - 'Conditional' => false, - 'HookToPrefix' => 'ord', - 'HookToSpecial' => '', - 'HookToEvent' => Array( 'OnUpdateCart', 'OnCheckout' ), - 'DoPrefix' => '', - 'DoSpecial' => '', - 'DoEvent' => 'OnApplyGiftCertificate', - ), - ), - 'QueryString' => Array ( 1 => 'id', 2 => 'Page', Index: branches/5.2.x/units/gift_certificates/gift_certificates_eh.php =================================================================== diff -u -N -r14594 -r14641 --- branches/5.2.x/units/gift_certificates/gift_certificates_eh.php (.../gift_certificates_eh.php) (revision 14594) +++ branches/5.2.x/units/gift_certificates/gift_certificates_eh.php (.../gift_certificates_eh.php) (revision 14641) @@ -1,6 +1,6 @@ Application->GetVar('giftcert_code'); - if ($code == '') { - return ; - } - - $object =& $event->getObject(Array('skip_autoload' => true)); - $object->Load($code, 'Code'); - - if (!$object->isLoaded()) { - $event->status = kEvent::erFAIL; - $this->Application->SetVar('set_checkout_error', 104); - $event->redirect = false; // check!!! - return ; - } - - $expire_date = $object->GetDBField('Expiration'); - $debit = $object->GetDBField('Debit'); - if( $object->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) || - ($debit <= 0)) - { - $event->status = kEvent::erFAIL; - $this->Application->SetVar('set_checkout_error', 105); - $event->redirect->false; - return ; - } - - $this->Application->setUnitOption('ord', 'AutoLoad', true); - $order =& $this->Application->recallObject('ord'); - $order->SetDBField('GiftCertificateId', $object->GetDBField('GiftCertificateId')); - $order->Update(); - - $this->Application->SetVar('set_checkout_error', 110); - } - - /** * Prepare temp tables for creating new item * but does not create it. Actual create is * done in OnPreSaveCreated