Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1123671
order_calculator.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Sep 2, 1:26 PM
Size
23 KB
Mime Type
text/x-php
Expires
Thu, Sep 4, 1:26 PM (1 d, 13 h)
Engine
blob
Format
Raw Data
Handle
726756
Attached To
rMINC Modules.In-Commerce
order_calculator.php
View Options
<?php
/**
* @version $Id: order_calculator.php 16775 2023-11-22 08:51:48Z alex $
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined
(
'FULL_PATH'
)
or
die
(
'restricted access!'
);
/**
* Performs order price calculations
*
*/
class
OrderCalculator
extends
kBase
{
/**
* Order manager instance
*
* @var OrderManager
*/
protected
$manager
=
null
;
/**
* Items, associated with current order
*
* @var Array
*/
protected
$items
=
Array
();
/**
* Creates new clean instance of calculator
*
*/
public
function
__construct
()
{
parent
::
__construct
();
$this
->
reset
();
}
/**
* Sets order manager instance to calculator
*
* @param OrderManager $manager
*/
public
function
setManager
(&
$manager
)
{
$this
->
manager
=&
$manager
;
}
public
function
reset
()
{
$this
->
items
=
Array
();
}
/**
* Returns order object used in order manager
*
* @return OrdersItem
*/
protected
function
&
getOrder
()
{
$order
=&
$this
->
manager
->
getOrder
();
return
$order
;
}
/**
* 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 protected
*/
protected
function
setError
(
$error_type
,
$error_code
,
$product_id
=
null
)
{
$this
->
manager
->
setError
(
$error_type
,
$error_code
,
$product_id
);
}
/**
* Perform order calculations and prepares operations for order manager
*
*/
public
function
calculate
()
{
$this
->
queryItems
();
$this
->
groupItems
();
$this
->
generateOperations
();
$this
->
applyWholeOrderFlatDiscount
();
}
/**
* Groups order items, when requested
*
* @return Array
*/
protected
function
groupItems
()
{
$skipped_items
=
Array
();
foreach
(
$this
->
items
as
$item_id
=>
$item_data
)
{
if
(
in_array
(
$item_id
,
$skipped_items
)
)
{
continue
;
}
$group_items
=
$this
->
getItemsToGroupWith
(
$item_id
);
if
(!
$group_items
)
{
continue
;
}
foreach
(
$group_items
as
$group_item_id
)
{
$this
->
items
[
$item_id
][
'Quantity'
]
+=
$this
->
items
[
$group_item_id
][
'Quantity'
];
$this
->
items
[
$group_item_id
][
'Quantity'
]
=
0
;
}
$skipped_items
=
array_merge
(
$skipped_items
,
$group_items
);
}
}
/**
* Returns order item ids, that can be grouped with given order item id
*
* @param int $target_item_id
* @return Array
* @see OrderCalculator::canBeGrouped
*/
protected
function
getItemsToGroupWith
(
$target_item_id
)
{
$ret
=
Array
();
foreach
(
$this
->
items
as
$item_id
=>
$item_data
)
{
if
(
$this
->
canBeGrouped
(
$this
->
items
[
$item_id
],
$this
->
items
[
$target_item_id
])
)
{
$ret
[]
=
$item_id
;
}
}
return
array_diff
(
$ret
,
Array
(
$target_item_id
));
}
/**
* Checks if 2 given order items can be grouped together
*
* @param Array $src_item
* @param Array $dst_item
* @return bool
*/
public
function
canBeGrouped
(
$src_item
,
$dst_item
)
{
if
(
$dst_item
[
'Type'
]
!=
PRODUCT_TYPE_TANGIBLE
)
{
return
false
;
}
return
(
$src_item
[
'ProductId'
]
==
$dst_item
[
'ProductId'
])
&&
(
$src_item
[
'OptionsSalt'
]
==
$dst_item
[
'OptionsSalt'
]);
}
/**
* Retrieves order contents from database
*
*/
protected
function
queryItems
()
{
$poc_table
=
$this
->
Application
->
getUnitOption
(
'poc'
,
'TableName'
);
$query
=
' SELECT oi.ProductId, oi.OptionsSalt, oi.ItemData, oi.Quantity,
IF(p.InventoryStatus = '
.
ProductInventory
::
BY_OPTIONS
.
', poc.QtyInStock, p.QtyInStock) AS QtyInStock,
p.QtyInStockMin, p.BackOrder, p.InventoryStatus,
p.Type, oi.OrderItemId
FROM '
.
$this
->
getTable
(
'orditems'
)
.
' AS oi
LEFT JOIN '
.
TABLE_PREFIX
.
'Products AS p ON oi.ProductId = p.ProductId
LEFT JOIN '
.
$poc_table
.
' poc ON (poc.CombinationCRC = oi.OptionsSalt) AND (oi.ProductId = poc.ProductId)
WHERE oi.OrderId = '
.
$this
->
getOrder
()->
GetID
();
$this
->
items
=
$this
->
Conn
->
Query
(
$query
,
'OrderItemId'
);
}
/**
* Generates operations and returns true, when something was changed
*
* @return bool
*/
protected
function
generateOperations
()
{
$this
->
manager
->
resetOperationTotals
();
foreach
(
$this
->
items
as
$item
)
{
$this
->
ensureMinQty
(
$item
);
$to_order
=
$back_order
=
0
;
$available
=
$this
->
getAvailableQty
(
$item
);
if
(
$this
->
allowBackordering
(
$item
)
)
{
// split order into order & backorder
if
(
$item
[
'BackOrder'
]
==
ProductBackorder
::
ALWAYS
)
{
$to_order
=
$available
=
0
;
$back_order
=
$item
[
'Quantity'
];
}
elseif
(
$item
[
'BackOrder'
]
==
ProductBackorder
::
AUTO
)
{
$to_order
=
$available
;
$back_order
=
$item
[
'Quantity'
]
-
$available
;
}
$qty
=
$to_order
+
$back_order
;
$price
=
$this
->
getPlainProductPrice
(
$item
,
$qty
);
$cost
=
$this
->
getProductCost
(
$item
,
$qty
);
$discount_info
=
$this
->
getDiscountInfo
(
$item
[
'ProductId'
],
$price
,
$qty
);
$this
->
manager
->
addOperation
(
$item
,
0
,
$to_order
,
$price
,
$cost
,
$discount_info
);
$this
->
manager
->
addOperation
(
$item
,
1
,
$back_order
,
$price
,
$cost
,
$discount_info
);
}
else
{
// store as normal order (and remove backorder)
// we could get here with backorder=never then we should order only what's available
$to_order
=
min
(
$item
[
'Quantity'
],
$available
);
$price
=
$this
->
getPlainProductPrice
(
$item
,
$to_order
);
$cost
=
$this
->
getProductCost
(
$item
,
$to_order
);
$discount_info
=
$this
->
getDiscountInfo
(
$item
[
'ProductId'
],
$price
,
$to_order
);
$this
->
manager
->
addOperation
(
$item
,
0
,
$to_order
,
$price
,
$cost
,
$discount_info
,
$item
[
'OrderItemId'
]);
$this
->
manager
->
addOperation
(
$item
,
1
,
0
,
$price
,
$cost
,
$discount_info
);
// remove backorder record
if
(
$to_order
<
$item
[
'Quantity'
])
{
// ordered less, then requested -> inform user
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'
);
}
}
}
}
}
/**
* Adds product to order (not to db)
*
* @param Array $item
* @param kCatDBItem $product
* @param int $qty
*/
public
function
addProduct
(
$item
,
&
$product
,
$qty
)
{
$this
->
updateItemDataFromProduct
(
$item
,
$product
);
$price
=
$this
->
getPlainProductPrice
(
$item
,
$qty
);
$cost
=
$this
->
getProductCost
(
$item
,
$qty
);
$discount_info
=
$this
->
getDiscountInfo
(
$item
[
'ProductId'
],
$price
,
$qty
);
$this
->
manager
->
addOperation
(
$item
,
0
,
$qty
,
$price
,
$cost
,
$discount_info
,
$item
[
'OrderItemId'
]
);
}
/**
* Apply whole order flat discount after sub-total been calculated
*
*/
protected
function
applyWholeOrderFlatDiscount
()
{
$sub_total_flat
=
$this
->
manager
->
getOperationTotal
(
'SubTotalFlat'
);
$flat_discount
=
min
(
$sub_total_flat
,
$this
->
getWholeOrderPlainDiscount
(
$global_discount_id
)
);
$coupon_flat_discount
=
min
(
$sub_total_flat
,
$this
->
getWholeOrderCouponDiscount
()
);
if
(
$coupon_flat_discount
&&
$coupon_flat_discount
>
$flat_discount
)
{
$global_discount_type
=
'coupon'
;
$flat_discount
=
$coupon_flat_discount
;
$global_discount_id
=
$coupon_id
;
}
else
{
$global_discount_type
=
'discount'
;
}
$sub_total
=
$this
->
manager
->
getOperationTotal
(
'SubTotal'
);
if
(
$sub_total_flat
-
$sub_total
<
$flat_discount
)
{
// individual item discounts together are smaller when order flat discount
$this
->
manager
->
setOperationTotal
(
'CouponDiscount'
,
$flat_discount
==
$coupon_flat_discount
?
$flat_discount
:
0
);
$this
->
manager
->
setOperationTotal
(
'SubTotal'
,
$sub_total_flat
-
$flat_discount
);
// replace discount for each operation
foreach
(
$this
->
operations
as
$index
=>
$operation
)
{
$discounted_price
=
(
$operation
[
'Price'
]
/
$sub_total_flat
)
*
$sub_total
;
$this
->
operations
[
$index
][
'DiscountInfo'
]
=
Array
(
$global_discount_id
,
$global_discount_type
,
$discounted_price
,
0
);
}
}
}
/**
* Returns discount information for given product price and qty
*
* @param int $product_id
* @param float $price
* @param int $qty
* @return Array
*/
protected
function
getDiscountInfo
(
$product_id
,
$price
,
$qty
)
{
$discounted_price
=
$this
->
getDiscountedProductPrice
(
$product_id
,
$price
,
$discount_id
);
$couponed_price
=
$this
->
getCouponDiscountedPrice
(
$product_id
,
$price
);
if
(
$couponed_price
<
$discounted_price
)
{
$discount_type
=
'coupon'
;
$discount_id
=
$coupon_id
;
$discounted_price
=
$couponed_price
;
$coupon_discount
=
(
$price
-
$couponed_price
)
*
$qty
;
}
else
{
$coupon_discount
=
0
;
$discount_type
=
'discount'
;
}
return
Array
(
$discount_id
,
$discount_type
,
$discounted_price
,
$coupon_discount
);
}
/**
* Returns product qty, available for ordering
*
* @param Array $item
* @return int
*/
protected
function
getAvailableQty
(
$item
)
{
if
(
$item
[
'InventoryStatus'
]
==
ProductInventory
::
DISABLED
)
{
// always available
return
$item
[
'Quantity'
]
*
2
;
}
return
max
(
0
,
$item
[
'QtyInStock'
]
-
$item
[
'QtyInStockMin'
]);
}
/**
* Checks, that product in given order item can be backordered
*
* @param Array $item
* @return bool
*/
protected
function
allowBackordering
(
$item
)
{
if
(
$item
[
'BackOrder'
]
==
ProductBackorder
::
ALWAYS
)
{
return
true
;
}
$available
=
$this
->
getAvailableQty
(
$item
);
$backordering
=
$this
->
Application
->
ConfigValue
(
'Comm_Enable_Backordering'
);
return
$backordering
&&
(
$item
[
'Quantity'
]
>
$available
)
&&
(
$item
[
'BackOrder'
]
==
ProductBackorder
::
AUTO
);
}
/**
* Make sure, that user can't order less, then minimal required qty of product
*
* @param Array $item
*/
protected
function
ensureMinQty
(&
$item
)
{
$sql
=
'SELECT MIN(MinQty)
FROM '
.
TABLE_PREFIX
.
'ProductsPricing
WHERE ProductId = '
.
$item
[
'ProductId'
];
$min_qty
=
max
(
1
,
$this
->
Conn
->
GetOne
(
$sql
));
$qty
=
$item
[
'Quantity'
];
if
(
$qty
>
0
&&
$qty
<
$min_qty
)
{
// qty in cart increased to meat minimal qry requirements of given product
$this
->
setError
(
OrderCheckoutErrorType
::
PRODUCT
,
OrderCheckoutError
::
QTY_CHANGED_TO_MINIMAL
,
$item
[
'ProductId'
]
.
':'
.
$item
[
'OptionsSalt'
]
.
':0:Quantity'
);
$item
[
'Quantity'
]
=
$min_qty
;
}
}
/**
* Return product price for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
public
function
getPlainProductPrice
(
$item
,
$qty
)
{
$item_data
=
$this
->
getItemData
(
$item
);
if
(
isset
(
$item_data
[
'ForcePrice'
])
)
{
return
$item_data
[
'ForcePrice'
];
}
$pricing_id
=
$this
->
getPriceBracketByQty
(
$item
,
$qty
);
$sql
=
'SELECT Price
FROM '
.
TABLE_PREFIX
.
'ProductsPricing
WHERE PriceId = '
.
$pricing_id
;
$price
=
(
float
)
$this
->
Conn
->
GetOne
(
$sql
);
if
(
isset
(
$item_data
[
'Options'
])
)
{
$price
+=
$this
->
getOptionPriceAddition
(
$price
,
$item_data
);
$price
=
$this
->
getCombinationPriceOverride
(
$price
,
$item_data
);
}
return
max
(
$price
,
0
);
}
/**
* Return product cost for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
public
function
getProductCost
(
$item
,
$qty
)
{
$pricing_id
=
$this
->
getPriceBracketByQty
(
$item
,
$qty
);
$sql
=
'SELECT Cost
FROM '
.
TABLE_PREFIX
.
'ProductsPricing
WHERE PriceId = '
.
$pricing_id
;
return
(
float
)
$this
->
Conn
->
GetOne
(
$sql
);
}
/**
* Return product price for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
protected
function
getPriceBracketByQty
(
$item
,
$qty
)
{
$orderby_clause
=
''
;
$where_clause
=
Array
();
$product_id
=
$item
[
'ProductId'
];
if
(
$this
->
usePriceBrackets
(
$item
)
)
{
$user_id
=
$this
->
getOrder
()->
GetDBField
(
'PortalUserId'
);
$where_clause
=
Array
(
'GroupId IN ('
.
$this
->
Application
->
getUserGroups
(
$user_id
)
.
')'
,
'pp.ProductId = '
.
$product_id
,
'pp.MinQty <= '
.
$qty
,
$qty
.
' < pp.MaxQty OR pp.MaxQty = -1'
,
);
$orderby_clause
=
$this
->
getPriceBracketOrderClause
(
$user_id
);
}
else
{
$item_data
=
$this
->
getItemData
(
$item
);
$where_clause
=
Array
(
'pp.ProductId = '
.
$product_id
,
'pp.PriceId = '
.
$this
->
getPriceBracketFromRequest
(
$product_id
,
$item_data
),
);
}
$sql
=
'SELECT pp.PriceId
FROM '
.
TABLE_PREFIX
.
'ProductsPricing AS pp
LEFT JOIN '
.
TABLE_PREFIX
.
'Products AS p ON p.ProductId = pp.ProductId
WHERE ('
.
implode
(
') AND ('
,
$where_clause
)
.
')'
;
if
(
$orderby_clause
)
{
$sql
.=
' ORDER BY '
.
$orderby_clause
;
}
return
(
float
)
$this
->
Conn
->
GetOne
(
$sql
);
}
/**
* Checks if price brackets should be used in price calculations
*
* @param Array $item
* @return bool
*/
protected
function
usePriceBrackets
(
$item
)
{
return
$item
[
'Type'
]
==
PRODUCT_TYPE_TANGIBLE
;
}
/**
* Return product pricing id for given product.
* If not passed - return primary pricing ID
*
* @param int $product_id
* @return int
*/
public
function
getPriceBracketFromRequest
(
$product_id
,
$item_data
)
{
if
(
!
is_array
(
$item_data
)
)
{
$item_data
=
unserialize
(
$item_data
);
}
// remembered pricing during checkout
if
(
isset
(
$item_data
[
'PricingId'
])
&&
$item_data
[
'PricingId'
]
)
{
return
$item_data
[
'PricingId'
];
}
// selected pricing from product detail page
$price_id
=
$this
->
Application
->
GetVar
(
'pr_id'
);
if
(
$price_id
)
{
return
$price_id
;
}
$sql
=
'SELECT PriceId
FROM '
.
TABLE_PREFIX
.
'ProductsPricing
WHERE ProductId = '
.
$product_id
.
' AND IsPrimary = 1'
;
return
$this
->
Conn
->
GetOne
(
$sql
);
}
/**
* Returns order clause for price bracket selection based on configration
*
* @param int $user_id
* @return string
*/
protected
function
getPriceBracketOrderClause
(
$user_id
)
{
if
(
$this
->
Application
->
ConfigValue
(
'Comm_PriceBracketCalculation'
)
==
1
)
{
// if we have to stick to primary group, then its pricing will go first,
// but if there is no pricing for primary group, then next optimal will be taken
/** @var UserHelper $user_helper */
$user_helper
=
$this
->
Application
->
recallObject
(
'UserHelper'
);
$primary_group
=
$user_helper
->
getPrimaryGroup
(
$user_id
);
return
'( IF(GroupId = '
.
$primary_group
.
', 1, 2) ) ASC, pp.Price ASC'
;
}
return
'pp.Price ASC'
;
}
/**
* Returns addition to product price based on used product option
*
* @param float $price
* @param Array $item_data
* @return float
*/
protected
function
getOptionPriceAddition
(
$price
,
$item_data
)
{
$addition
=
0
;
/** @var kProductOptionsHelper $opt_helper */
$opt_helper
=
$this
->
Application
->
recallObject
(
'kProductOptionsHelper'
);
foreach
(
$item_data
[
'Options'
]
as
$opt
=>
$val
)
{
$sql
=
'SELECT *
FROM '
.
TABLE_PREFIX
.
'ProductOptions
WHERE ProductOptionId = '
.
$opt
;
$data
=
$this
->
Conn
->
GetRow
(
$sql
);
$parsed
=
$opt_helper
->
ExplodeOptionValues
(
$data
);
if
(
!
$parsed
)
{
continue
;
}
if
(
is_array
(
$val
)
)
{
foreach
(
$val
as
$a_val
)
{
$addition
+=
$this
->
formatPrice
(
$a_val
,
$price
,
$parsed
);
}
}
else
{
$addition
+=
$this
->
formatPrice
(
$val
,
$price
,
$parsed
);
}
}
return
$addition
;
}
protected
function
formatPrice
(
$a_val
,
$price
,
$parsed
)
{
$a_val
=
kUtil
::
unescape
(
$a_val
,
kUtil
::
ESCAPE_HTML
);
// TODO: Not sure why we're unescaping.
$addition
=
0
;
$conv_prices
=
$parsed
[
'Prices'
];
$conv_price_types
=
$parsed
[
'PriceTypes'
];
if
(
isset
(
$conv_prices
[
$a_val
])
&&
$conv_prices
[
$a_val
]
)
{
if
(
$conv_price_types
[
$a_val
]
==
'$'
)
{
$addition
+=
$conv_prices
[
$a_val
];
}
elseif
(
$conv_price_types
[
$a_val
]
==
'%'
)
{
$addition
+=
$price
*
$conv_prices
[
$a_val
]
/
100
;
}
}
return
$addition
;
}
/**
* Returns product price after applying combination price override
*
* @param float $price
* @param Array $item_data
* @return float
*/
protected
function
getCombinationPriceOverride
(
$price
,
$item_data
)
{
$combination_salt
=
$this
->
generateOptionsSalt
(
$item_data
[
'Options'
]
);
if
(!
$combination_salt
)
{
return
$price
;
}
$sql
=
'SELECT *
FROM '
.
TABLE_PREFIX
.
'ProductOptionCombinations
WHERE CombinationCRC = '
.
$combination_salt
;
$combination
=
$this
->
Conn
->
GetRow
(
$sql
);
if
(!
$combination
)
{
return
$price
;
}
switch
(
$combination
[
'PriceType'
]
)
{
case
OptionCombinationPriceType
::
EQUALS
:
return
$combination
[
'Price'
];
break
;
case
OptionCombinationPriceType
::
FLAT
:
return
$price
+
$combination
[
'Price'
];
break
;
case
OptionCombinationPriceType
::
PECENT
:
return
$price
*
(
1
+
$combination
[
'Price'
]
/
100
);
break
;
}
return
$price
;
}
/**
* Generates salt for given option set
*
* @param Array $options
* @return int
*/
public
function
generateOptionsSalt
(
$options
)
{
/** @var kProductOptionsHelper $opt_helper */
$opt_helper
=
$this
->
Application
->
recallObject
(
'kProductOptionsHelper'
);
return
$opt_helper
->
OptionsSalt
(
$options
,
true
);
}
/**
* Return product price for given qty, taking possible discounts into account
*
* @param int $product_id
* @param int $price
* @param int $discount_id
* @return float
*/
public
function
getDiscountedProductPrice
(
$product_id
,
$price
,
&
$discount_id
)
{
$discount_id
=
0
;
$user_id
=
$this
->
getOrder
()->
GetDBField
(
'PortalUserId'
);
$join_clause
=
Array
(
'd.DiscountId = di.DiscountId'
,
'di.ItemType = '
.
DiscountItemType
::
PRODUCT
.
' OR (di.ItemType = '
.
DiscountItemType
::
WHOLE_ORDER
.
' AND d.Type = '
.
DiscountType
::
PERCENT
.
')'
,
'd.Status = '
.
STATUS_ACTIVE
,
'd.GroupId IN ('
.
$this
->
Application
->
getUserGroups
(
$user_id
)
.
')'
,
'd.Start IS NULL OR d.Start < '
.
$this
->
getOrder
()->
GetDBField
(
'OrderDate'
),
'd.End IS NULL OR d.End > '
.
$this
->
getOrder
()->
GetDBField
(
'OrderDate'
),
);
$sql
=
'SELECT
ROUND(CASE d.Type
WHEN '
.
DiscountType
::
FLAT
.
' THEN '
.
$price
.
' - d.Amount
WHEN '
.
DiscountType
::
PERCENT
.
' THEN '
.
$price
.
' * (1 - d.Amount / 100)
ELSE '
.
$price
.
'
END, 4), d.DiscountId
FROM '
.
TABLE_PREFIX
.
'Products AS p
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsDiscountItems AS di ON (di.ItemResourceId = p.ResourceId) OR (di.ItemType = '
.
DiscountItemType
::
WHOLE_ORDER
.
')
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsDiscounts AS d ON ('
.
implode
(
') AND ('
,
$join_clause
)
.
')
WHERE (p.ProductId = '
.
$product_id
.
') AND (d.DiscountId IS NOT NULL)'
;
$pricing
=
$this
->
Conn
->
GetCol
(
$sql
,
'DiscountId'
);
if
(!
$pricing
)
{
return
$price
;
}
// get minimal price + discount
$discounted_price
=
min
(
$pricing
);
$pricing
=
array_flip
(
$pricing
);
$discount_id
=
$pricing
[
$discounted_price
];
// optimal discount, but prevent negative price
return
max
(
min
(
$discounted_price
,
$price
),
0
);
}
public
function
getWholeOrderPlainDiscount
(&
$discount_id
)
{
$discount_id
=
0
;
$user_id
=
$this
->
getOrder
()->
GetDBField
(
'PortalUserId'
);
$join_clause
=
Array
(
'd.DiscountId = di.DiscountId'
,
'di.ItemType = '
.
DiscountItemType
::
WHOLE_ORDER
.
' AND d.Type = '
.
DiscountType
::
FLAT
,
'd.Status = '
.
STATUS_ACTIVE
,
'd.GroupId IN ('
.
$this
->
Application
->
getUserGroups
(
$user_id
)
.
')'
,
'd.Start IS NULL OR d.Start < '
.
$this
->
getOrder
()->
GetDBField
(
'OrderDate'
),
'd.End IS NULL OR d.End > '
.
$this
->
getOrder
()->
GetDBField
(
'OrderDate'
),
);
$sql
=
'SELECT d.Amount AS Discount, d.DiscountId
FROM '
.
TABLE_PREFIX
.
'ProductsDiscountItems AS di
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsDiscounts AS d ON ('
.
implode
(
') AND ('
,
$join_clause
)
.
')
WHERE d.DiscountId IS NOT NULL'
;
$pricing
=
$this
->
Conn
->
GetCol
(
$sql
,
'DiscountId'
);
if
(!
$pricing
)
{
return
0
;
}
$discounted_price
=
max
(
$pricing
);
$pricing
=
array_flip
(
$pricing
);
$discount_id
=
$pricing
[
$discounted_price
];
return
max
(
$discounted_price
,
0
);
}
public
function
getCouponDiscountedPrice
(
$product_id
,
$price
)
{
if
(
!
$this
->
getCoupon
()
)
{
return
$price
;
}
$join_clause
=
Array
(
'c.CouponId = ci.CouponId'
,
'ci.ItemType = '
.
CouponItemType
::
PRODUCT
.
' OR (ci.ItemType = '
.
CouponItemType
::
WHOLE_ORDER
.
' AND c.Type = '
.
CouponType
::
PERCENT
.
')'
,
);
$sql
=
'SELECT
MIN(
ROUND(CASE c.Type
WHEN '
.
CouponType
::
FLAT
.
' THEN '
.
$price
.
' - c.Amount
WHEN '
.
CouponType
::
PERCENT
.
' THEN '
.
$price
.
' * (1 - c.Amount / 100)
ELSE '
.
$price
.
'
END, 4)
)
FROM '
.
TABLE_PREFIX
.
'Products AS p
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsCouponItems AS ci ON (ci.ItemResourceId = p.ResourceId) OR (ci.ItemType = '
.
CouponItemType
::
WHOLE_ORDER
.
')
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsCoupons AS c ON ('
.
implode
(
') AND ('
,
$join_clause
)
.
')
WHERE p.ProductId = '
.
$product_id
.
' AND ci.CouponId = '
.
$this
->
getCoupon
()
.
'
GROUP BY p.ProductId'
;
$coupon_price
=
$this
->
Conn
->
GetOne
(
$sql
);
if
(
$coupon_price
===
false
)
{
return
$price
;
}
return
max
(
min
(
$price
,
$coupon_price
),
0
);
}
public
function
getWholeOrderCouponDiscount
()
{
if
(
!
$this
->
getCoupon
()
)
{
return
0
;
}
$where_clause
=
Array
(
'ci.CouponId = '
.
$this
->
getCoupon
(),
'ci.ItemType = '
.
CouponItemType
::
WHOLE_ORDER
,
'c.Type = '
.
CouponType
::
FLAT
,
);
$sql
=
'SELECT Amount
FROM '
.
TABLE_PREFIX
.
'ProductsCouponItems AS ci
LEFT JOIN '
.
TABLE_PREFIX
.
'ProductsCoupons AS c ON c.CouponId = ci.CouponId
WHERE ('
.
implode
(
') AND ('
,
$where_clause
)
.
')'
;
return
$this
->
Conn
->
GetOne
(
$sql
);
}
protected
function
getCoupon
()
{
return
$this
->
getOrder
()->
GetDBField
(
'CouponId'
);
}
/**
* Returns ItemData associated with given order item
*
* @param Array $item
* @return Array
*/
protected
function
getItemData
(
$item
)
{
$item_data
=
$item
[
'ItemData'
];
if
(
is_array
(
$item_data
)
)
{
return
$item_data
;
}
return
$item_data
?
unserialize
(
$item_data
)
:
Array
();
}
/**
* Sets ItemData according to product
*
* @param Array $item
* @param kCatDBItem $product
*/
protected
function
updateItemDataFromProduct
(&
$item
,
&
$product
)
{
$item_data
=
$this
->
getItemData
(
$item
);
$item_data
[
'IsRecurringBilling'
]
=
$product
->
GetDBField
(
'IsRecurringBilling'
);
// it item is processed in order using new style, then put such mark in orderitem record
$processing_data
=
$product
->
GetDBField
(
'ProcessingData'
);
if
(
$processing_data
)
{
$processing_data
=
unserialize
(
$processing_data
);
if
(
isset
(
$processing_data
[
'HasNewProcessing'
])
)
{
$item_data
[
'HasNewProcessing'
]
=
1
;
}
}
$item
[
'ItemData'
]
=
serialize
(
$item_data
);
}
/**
* Returns table name according to order temp mode
*
* @param string $prefix
* @return string
*/
protected
function
getTable
(
$prefix
)
{
return
$this
->
manager
->
getTable
(
$prefix
);
}
}
Event Timeline
Log In to Comment