クラス
PurchaseSanityChecks
ソース ソース
ファイル: src/API/PurchaseSanityChecks.php
class PurchaseSanityChecks
{
/**
* Registers hooks
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @return void
*/
public static function init() {
add_filter('usces_filter_zaiko_check', [get_class(), 'checkComboSetItems'], 10, 2);
}
/**
* Runs validation checks for each combo-set item in the cart (if applicable)
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @param string $mes
* @param array $cart
* @return string
*/
public static function checkComboSetItems($mes, $cart) {
// just quit if none of the cart items are combo-sets
$found = false;
foreach ($cart as $crow) {
$sels = unserialize($crow['serial']);
if (!empty($sels['comboSetId']) && !empty($sels['comboSetItems'])) {
$found = true;
break;
}
}
if ($found === false) {
return $mes;
}
$divcheckmes = self::checkDivisions($cart);
if (!empty($divcheckmes)) {
return $divcheckmes;
}
return self::checkCartItemsInventory($mes, $cart);
}
/**
* Check combo-set and group item divisions to ensure compatability
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @param array $cart
* @return string
*/
public static function checkDivisions($cart) {
$mes = '';
foreach ($cart as $i => $cart_row) {
$sels = unserialize($cart_row['serial']);
$mapifiedselections = [];
$errors = [];
if (!empty($sels['comboSetId']) && !empty($sels['comboSetItems'])) {
foreach ($sels['comboSetItems'] as $giserial => $gitem) {
$data = unserialize($giserial);
$mapifiedselections[$data['groupId']][] = $data['itemId'];
$result = self::comboSetItemDivisionIsValid($sels['comboSetId'], $mapifiedselections);
if ($result instanceof GenericError) {
$errors[] = $result->message;
}
}
}
$mes .= self::constructIndexedErrorMessage($i, $errors);
}
return $mes;
}
/**
* Checks stock for each cart item and each group item in a combo-set
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @global \usc_e_shop $usces
* @param string $mes
* @param array $cart
* @return string
*/
public static function checkCartItemsInventory($mes, $cart) {
global $usces;
// just quit if none of the cart items are combo-sets
$found = false;
foreach ($cart as $crow) {
$sels = unserialize($crow['serial']);
if (!empty($sels['comboSetId']) && !empty($sels['comboSetItems'])) {
$found = true;
break;
}
}
if ($found === false) {
return $mes;
}
$mes = '';
$stocks = [];
foreach ($cart as $i => $cart_row) {
$rowmes = '';
$sels = unserialize($cart_row['serial']);
$cart_row = $cart[$i];
$post_id = $cart_row['post_id'];
$sku = $cart_row['sku'];
$sku_code = urldecode($cart_row['sku']);
$quant = isset($_POST['quant'][$i][$post_id][$sku]) ? (int)trim($_POST['quant'][$i][$post_id][$sku]) : $cart_row['quantity'];
$stock = $usces->getItemZaikoNum($post_id, $sku_code);
if (!isset($stocks[$post_id][$sku])) {
if (!\WCUtils::is_blank($stock)) {
$stocks[$post_id][$sku] = $stock;
} else {
$stocks[$post_id][$sku] = null;
}
}
$checkstock = $stocks[$post_id][$sku];
$stocks[$post_id][$sku] = $stocks[$post_id][$sku] - $quant;
$product = wel_get_product($post_id);
$itemRestriction = isset($product['itemRestriction']) ? $product['itemRestriction'] : '';
$itemOrderAcceptable = $usces->getItemOrderAcceptable($post_id);
$post_status = get_post_status($post_id);
if (1 > (int)$quant) {
$rowmes .= sprintf(__('Enter the correct amount for the No.%d item.', 'usces'), ($i + 1)) . '<br />';
} elseif (!$usces->is_item_zaiko($post_id, $sku_code) || ($itemOrderAcceptable != 1 && \WCUtils::is_zero($stock)) || 'publish' != $post_status) {
$rowmes .= sprintf(__('Sorry, No.%d item is sold out.', 'usces'), ($i + 1)) . '<br />';
} elseif ($quant > (int)$itemRestriction && !\WCUtils::is_blank($itemRestriction) && !\WCUtils::is_zero($itemRestriction)) {
$rowmes .= sprintf(__('This article is limited by %1$d at a time for the No.%2$d item.', 'usces'), $itemRestriction, ($i + 1)) . '<br />';
} elseif ($itemOrderAcceptable != 1 && 0 > $stocks[$post_id][$sku] && !\WCUtils::is_blank($stock)) {
$rowmes .= sprintf(__('Stock of No.%1$d item is remainder %2$d.', 'usces'), ($i + 1), $checkstock) . '<br />';
}
$mes .= $rowmes;
if ((int)$quant > 0 && empty($rowmes)) {
$gierrors = [];
if (!empty($sels['comboSetId']) && !empty($sels['comboSetItems'])) {
$mapifiedselections = [];
foreach ($sels['comboSetItems'] as $giserial => $gitem) {
$data = unserialize($giserial);
$mapifiedselections[$data['groupId']][] = $data['itemId'];
}
$result = self::comboSetSelectionsAreValid($sels['comboSetId'], $mapifiedselections);
if ($result instanceof GenericError) {
$message = $result->message;
if ($result->errorcode === ErrorStore::COMBO_GROUP_IS_REQUIRED) {
$message = __('Selections for this combo-set item have been updated. Please refresh the page and try again.', 'wcexics');
}
$gierrors[] = $message;
} else {
$giindex = 0;
foreach ($sels['comboSetItems'] as $giserial => $gitem) {
$gitemquant = $sels['comboSetItems'][$giserial]['quant'] * $quant;
$gicrow = (new \usces_cart())->key_unserialize($giserial);
$gipost_id = $gicrow['post_id'];
$gisku = $gicrow['sku'];
$gisku_code = urldecode($gicrow['sku']);
$stock = $usces->getItemZaikoNum($gipost_id, $gisku_code);
if (!isset($stocks[$gipost_id][$gisku])) {
if (!\WCUtils::is_blank($stock)) {
$stocks[$gipost_id][$gisku] = $stock;
} else {
$stocks[$gipost_id][$gisku] = null;
}
}
$checkstock = $stocks[$gipost_id][$gisku];
$stocks[$gipost_id][$gisku] = $stocks[$gipost_id][$gisku] - $gitemquant;
$giproduct = wel_get_product($gipost_id);
$itemRestriction = isset($giproduct['itemRestriction']) ? $giproduct['itemRestriction'] : '';
$itemOrderAcceptable = $usces->getItemOrderAcceptable($gipost_id);
$post_status = get_post_status($gipost_id);
if (!$usces->is_item_zaiko($gipost_id, $gisku_code) || ($itemOrderAcceptable != 1 && \WCUtils::is_zero($stock)) || 'publish' != $post_status) {
$gierrors[] = sprintf(__('Sorry, No.%d item is sold out.', 'usces'), ($giindex + 1));
} elseif ($gitemquant > (int)$itemRestriction && !\WCUtils::is_blank($itemRestriction) && !\WCUtils::is_zero($itemRestriction)) {
$gierrors[] = sprintf(__('This article is limited by %1$d at a time for the No.%2$d item.', 'usces'), $itemRestriction, ($giindex + 1));
} elseif ($itemOrderAcceptable != 1 && 0 > $stocks[$gipost_id][$gisku] && !\WCUtils::is_blank($stock)) {
$gierrors[] = sprintf(__('Stock of No.%1$d item is remainder %2$d.', 'usces'), ($giindex + 1), $checkstock);
}
$giindex++;
}
}
}
$mes .= self::constructIndexedErrorMessage($i, $gierrors);
}
}
return $mes;
}
/**
* Checks for existence of the combo-set. Also checks for the existence of each group and item and
* checks that required groups have at least one selection.
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @param int $comboSetId
* @param array $grouptoitemmap
* @return GenericError|true
*/
public static function comboSetSelectionsAreValid($comboSetId, $grouptoitemmap) {
$comboSetId = (int)$comboSetId;
$comboset = ComboSet::getComboSetById($comboSetId);
if ($comboset instanceof GenericError) {
return $comboset;
}
$grouptoitemmap = Cart::sanitizeComboSetSelections($grouptoitemmap);
// check that all required groups have selections
foreach ($comboset->getGroups() as $group) {
if ($group->getOptional() === false) {
// if the required group has at least one item but no item is selected, abort
if (count($group->getItems()) > 0 && empty($grouptoitemmap[$group->getId()])) {
return Master::getErrorStore()->getErrorResponse(
ErrorStore::COMBO_GROUP_IS_REQUIRED,
[$group],
[$group],
[$group]
);
}
}
}
// check that all selected groups and items exist
foreach ($grouptoitemmap as $groupid => $itemids) {
$group = ComboGroup::getComboGroupById($groupid);
// if the group does not exist OR it is not associated with the combo-set, abort
if ($group === null || $group->getComboSetId() !== $comboset->getId()) {
return Master::getErrorStore()->getErrorResponse(
ErrorStore::COMBO_GROUP_NOT_FOUND,
[$groupid],
[],
[$groupid]
);
}
foreach ($itemids as $itemid) {
$item = GroupItem::getGroupItemById($itemid);
// if the item does not exist OR it is not associated with the group, abort
if ($item === null || $item->getGroupId() !== $group->getId()) {
return Master::getErrorStore()->getErrorResponse(
ErrorStore::GROUP_ITEM_NOT_FOUND,
[$itemid],
[],
[$itemid]
);
}
}
}
return true;
}
/**
* Checks validity of item divisions
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @global \usc_e_shop $usces
* @param int $comboSetId
* @param array $grouptoitemmap
* @return GenericError|true
*/
public static function comboSetItemDivisionIsValid($comboSetId, $grouptoitemmap) {
global $usces;
$comboSetId = (int)$comboSetId;
$comboset = ComboSet::getComboSetById($comboSetId);
if ($comboset instanceof GenericError) {
return $comboset;
}
$grouptoitemmap = Cart::sanitizeComboSetSelections($grouptoitemmap);
$division = strtolower((string)$usces->getItemDivision($comboset->getPostId()));
if ($division === 'shipped') {
$notshipped = false;
foreach ($grouptoitemmap as $groupid => $itemids) {
$group = ComboGroup::getComboGroupById($groupid);
if (empty($group)) {
continue;
}
foreach ($itemids as $itemid) {
$item = GroupItem::getGroupItemById($itemid);
if (empty($item)) {
continue;
}
$gidivision = strtolower((string)$usces->getItemDivision($item->getPostId()));
if ($gidivision !== $division) {
$notshipped = $gidivision;
break;
}
}
if ($notshipped !== false) {
break;
}
}
if ($notshipped !== false) {
$dvars = [
$division,
$gidivision,
__('The division of each group item for a combo-set with division "shipped" must also be "shipped"', 'wcexics'),
];
$error = Master::getErrorStore()->getErrorResponse(
ErrorStore::COMBO_SET_DIVISION_INVALID,
$dvars,
[],
$dvars
);
$error->setLogger(new Logger())->setData(array_merge(
$comboset->getDataForLog(),
['groupItemSelections' => $grouptoitemmap]
));
$error->logger->error($error);
return $error;
}
}
return true;
}
/**
* Checks validity of item charge types
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @global \usc_e_shop $usces
* @param int $comboSetId
* @param array $grouptoitemmap
* @return GenericError|true
*/
public static function comboSetItemChargeTypeIsValid($comboSetId, $grouptoitemmap) {
global $usces;
$comboSetId = (int)$comboSetId;
$comboset = ComboSet::getComboSetById($comboSetId);
if ($comboset instanceof GenericError) {
return $comboset;
}
$chargetype = strtolower((string)$usces->getItemChargingType($comboset->getPostId()));
if ($chargetype === 'once') {
$grouptoitemmap = Cart::sanitizeComboSetSelections($grouptoitemmap);
foreach ($grouptoitemmap as $groupid => $itemids) {
$group = ComboGroup::getComboGroupById($groupid);
if (empty($group)) {
continue;
}
foreach ($itemids as $itemid) {
$item = GroupItem::getGroupItemById($itemid);
if (empty($item)) {
continue;
}
$gichargetype = strtolower((string)$usces->getItemChargingType($item->getPostId()));
if ($gichargetype === 'continue') {
$mvars = [__('Normal Charging', 'dlseller'), __('Continuation Charging', 'dlseller')];
$error = Master::getErrorStore()->getErrorResponse(
ErrorStore::INVALID_CHARGE_TYPE_COMBINATION,
$mvars,
[],
$mvars
);
$error->setLogger(new Logger())->setData([
'comboSet' => $comboset->getDataForLog(),
'welitemSkuMetaId' => $item->getSkuMetaId(),
]);
return $error;
}
}
}
}
return true;
}
/**
* Constructs an indexed error message for the cart page
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @param int $i
* @param array $gierrors
* @return string
*/
public static function constructIndexedErrorMessage($i, $gierrors) {
$mes = '';
if (empty($gierrors)) {
return $mes;
}
$mes .= '<div class="wcexics combo-set-cart-errors">';
$mes .= '<div class="combo-set-cart-errors__title">' . sprintf(
// translators: The combo-set cart item index
__('Combo-set No. %d encountered the following problems:', 'wcexics'),
($i + 1)
);
$mes .= '</div>';
$mes .= '<ul>';
$gierrors = array_map(function ($e) {
return '<li>' . $e . '</li>';
}, $gierrors);
$mes .= join('', $gierrors);
$mes .= '</ul>';
$mes .= '</div>';
return $mes;
}
/**
* Exits to item page with an error message
*
* This method mirrors the exit logic in `$usces->incart_check()`. If only
* the exit logic existed in its own function we could call it directly......
*
* @author Evan D Shaw <evandanielshaw@gmail.com>
* @param string $message
* @return void
*/
public static function exitAddToCartWithError($message) {
$ids = array_keys($_POST['inCart']);
$post_id = $ids[0];
$skus = array_keys($_POST['inCart'][$post_id]);
$sku = urldecode($skus[0]);
$_SESSION['usces_singleitem']['error_message'] = [$post_id => [$sku => $message]];
$url = esc_url($_POST['usces_referer']);
if (false === strpos($_POST['usces_referer'], 'http')) {
$parse_url = parse_url(get_home_url());
$port = '';
if ($parse_url['host'] === 'localhost' && !empty($parse_url['port'])) {
$port = ':' . $parse_url['port'];
}
$url = $parse_url['scheme'] . '://' . $parse_url['host'] . $port . $url;
}
header('location: ' . $url . apply_filters('usces_filter_incart_redirect', '#cart_button', $post_id, $sku));
exit;
}
}
- checkCartItemsInventory — Checks stock for each cart item and each group item in a combo-set
- checkComboSetItems — Runs validation checks for each combo-set item in the cart (if applicable)
- checkDivisions — Check combo-set and group item divisions to ensure compatability
- comboSetItemChargeTypeIsValid — Checks validity of item charge types
- comboSetItemDivisionIsValid — Checks validity of item divisions
- comboSetSelectionsAreValid — Checks for existence of the combo-set. Also checks for the existence of each group and item and checks that required groups have at least one selection.
- constructIndexedErrorMessage — Constructs an indexed error message for the cart page
- exitAddToCartWithError — Exits to item page with an error message
- init — Registers hooks