4061 lines
135 KiB
PHP
4061 lines
135 KiB
PHP
<?php
|
|
/**
|
|
* Order placed
|
|
*
|
|
* (also known as listorders)
|
|
*
|
|
* @package Crunchbutton.Order
|
|
* @category model
|
|
*
|
|
* @property notes The comments the user set for the order
|
|
*/
|
|
class Crunchbutton_Order extends Crunchbutton_Order_Trackchange {
|
|
|
|
const PAY_TYPE_CASH = 'cash';
|
|
const PAY_TYPE_CREDIT_CARD = 'card';
|
|
const PAY_TYPE_CAMPUS_CASH = 'campus_cash';
|
|
const PAY_TYPE_APPLE_PAY = 'applepay';
|
|
const SHIPPING_DELIVERY = 'delivery';
|
|
const SHIPPING_TAKEOUT = 'takeout';
|
|
const TIP_PERCENT = 'percent';
|
|
const TIP_NUMBER = 'number';
|
|
|
|
const CONFIG_KEY_GEO_TICKET_DELIVERY_RADIUS = 'order_ticket_radius';
|
|
const CONFIG_KEY_GEO_TICKET_NOT_GET_ORDERS = 'order_ticket_geo';
|
|
|
|
const ORDER_LOADING_PHRASE_KEY = 'order-loading-phrase';
|
|
|
|
const PROCESS_TYPE_RESTAURANT = 'restaurant';
|
|
const PROCESS_TYPE_WEB = 'web';
|
|
const PROCESS_TYPE_ADMIN = 'admin';
|
|
|
|
const STATUS_REFUNDED_TOTAL = 'Refunded';
|
|
const STATUS_REFUNDED_PROCESSING = 'Processing Refunds';
|
|
const STATUS_REFUNDED_PARTIALLY = 'Partially Refunded';
|
|
|
|
const PRE_ORDER_INTERVAL = '+ 60 minutes';
|
|
const PRE_ORDER_DELIVERY_WINDOW = '+ 15 minutes';
|
|
|
|
/**
|
|
* Process an order
|
|
*
|
|
*
|
|
* @param array $params
|
|
*
|
|
* @return string|Ambigous <>|boolean
|
|
*
|
|
* @todo Add more security here
|
|
* @todo It looks like if there are orders not set as delivery nor takeout, we need to log them.
|
|
*/
|
|
public function process($params, $processType = 'web'){
|
|
|
|
$this->campus_cash = ( $params['pay_type'] == self::PAY_TYPE_CAMPUS_CASH );
|
|
|
|
$this->pay_type = ($params['pay_type'] == 'cash') ? 'cash' : 'card';
|
|
$this->address = $params['address'];
|
|
$this->phone = $params['phone'];
|
|
$this->name = $params['name'];
|
|
$this->notes = $params['notes'];
|
|
$this->local_gid = $params['local_gid'];
|
|
$this->id_restaurant = $params['restaurant'];
|
|
|
|
$this->preordered = ( $params['preordered'] ? 1 : 0 );
|
|
if( $this->preordered ){
|
|
$deliveryDay = $params['deliveryDay'];
|
|
$deliveryHour = $params['deliveryHour'];
|
|
$this->_date_delivery = new DateTime( $deliveryDay . ' ' . $deliveryHour, new DateTimeZone( $this->restaurant()->timezone ) );
|
|
$this->_date_delivery->setTimezone( new DateTimeZone( c::config()->timezone ) );
|
|
$this->date_delivery = $this->_date_delivery->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
// Log the order - process started
|
|
Log::debug([
|
|
'action' => 'process started',
|
|
'address' => $params['address'],
|
|
'phone' => $params['phone'],
|
|
'pay_type' => $params['pay_type'],
|
|
'tip' => $params['tip'],
|
|
'autotip' => $params['autotip'],
|
|
'autotip_value' => $params['autotip_value'],
|
|
'name' => $params['name'],
|
|
'user_id' => c::user()->id_user,
|
|
'delivery_type' => $params['delivery_type'],
|
|
'restaurant' => $params['restaurant'],
|
|
'notes' => $params['notes'],
|
|
'preordered' => $params['preordered'],
|
|
'deliveryDay' => $params['deliveryDay'],
|
|
'deliveryHour' => $params['deliveryHour'],
|
|
'restaurant' => $this->restaurant()->name,
|
|
'timezone' => $this->restaurant()->timezone,
|
|
'cart' => $params['cart'],
|
|
'campus_cash' => $this->campus_cash,
|
|
'type' => 'order-log'
|
|
]);
|
|
|
|
// set delivery as default,
|
|
$this->delivery_type = self::SHIPPING_DELIVERY;
|
|
if ($params['delivery_type'] == self::SHIPPING_TAKEOUT) {
|
|
$this->delivery_type = self::SHIPPING_TAKEOUT;
|
|
} elseif ($params['delivery_type'] != self::SHIPPING_DELIVERY ) {
|
|
// log when an order is not delivery nor takeout
|
|
Crunchbutton_Log::error([
|
|
'type' => 'wrong-delivery-type',
|
|
'order_params' => $params,
|
|
]);
|
|
}
|
|
|
|
if ($params['pay_type'] == self::PAY_TYPE_CREDIT_CARD) {
|
|
$params['pay_type'] = self::PAY_TYPE_APPLE_PAY;
|
|
}
|
|
|
|
if ( $params['processor'] && $params['pay_type'] == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && Crunchbutton_User_Payment_Type::processor() != $params['processor']) {
|
|
$errors['processor'] = 'We recently upgraded our credit card processing security. Please press "Place Order" again to automagicly use our fancy new system.';
|
|
$errors['set-processor'] = Crunchbutton_User_Payment_Type::processor();
|
|
}
|
|
|
|
if( $this->preordered ){
|
|
if( !$params['deliveryDay'] ){
|
|
$errors['preorder_day'] = 'Please select the desired delivery day.';
|
|
}
|
|
if( !$params['deliveryHour'] ){
|
|
$errors['preorder_hour'] = 'Please select the desired delivery hour.';
|
|
}
|
|
if( $this->_date_delivery ){
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
if( $now->format('YmdHis') > $this->_date_delivery->format('YmdHis') ){
|
|
$errors['preorder_prev_date'] = 'The desired delivery hour should not be in the past.';
|
|
}
|
|
if(!$this->restaurant()->validatePreOrderDate($this->_date_delivery->format('Y-m-d H:i:s'))){
|
|
$errors['preorder_date'] = 'Oops, there is something wrong with the desired time!';
|
|
}
|
|
}
|
|
}
|
|
|
|
if( $processType == static::PROCESS_TYPE_WEB ){
|
|
// Check if the restaurant is active #2938
|
|
if(!$this->restaurant()->active){
|
|
$errors['inactive'] = 'This restaurant is not accepting orders.';
|
|
}
|
|
}
|
|
|
|
if( $processType == static::PROCESS_TYPE_RESTAURANT ){
|
|
// Check if the restaurant is active for restaurant order placement
|
|
// https://github.com/crunchbutton/crunchbutton/issues/3350#issuecomment-48255149
|
|
if(!$this->restaurant()->active_restaurant_order_placement){
|
|
$errors['inactive'] = 'This restaurant is not accepting orders.';
|
|
}
|
|
}
|
|
|
|
// Check if the restaurant delivery #2464
|
|
if( $this->delivery_type == self::SHIPPING_DELIVERY ){
|
|
if(!$this->restaurant()->delivery && $this->restaurant()->takeout){
|
|
$this->delivery_type = self::SHIPPING_TAKEOUT;
|
|
} else {
|
|
// log when an order is not delivery nor takeout
|
|
Crunchbutton_Log::error([
|
|
'type' => 'wrong-delivery-type',
|
|
'order_params' => $params,
|
|
]);
|
|
}
|
|
}
|
|
|
|
if( $this->delivery_type == self::SHIPPING_TAKEOUT ){
|
|
if(!$this->restaurant()->takeout && $this->restaurant()->delivery){
|
|
$this->delivery_type = self::SHIPPING_DELIVERY;
|
|
} else {
|
|
// log when an order is not delivery nor takeout
|
|
Crunchbutton_Log::error([
|
|
'type' => 'wrong-delivery-type',
|
|
'order_params' => $params,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$subtotal = 0;
|
|
$subtotal_plus_delivery_service_markup = 0;
|
|
|
|
$this->id_restaurant = $params['restaurant'];
|
|
|
|
$delivery_service_markup = ( $this->restaurant()->delivery_service_markup ) ? $this->restaurant()->delivery_service_markup : 0;
|
|
$this->delivery_service_markup = $delivery_service_markup;
|
|
|
|
// #7404
|
|
$food_error = false;
|
|
$foodErrorDishes = [];
|
|
|
|
if ($processType == static::PROCESS_TYPE_RESTAURANT) {
|
|
$subtotal = $params['subtotal'];
|
|
$delivery_service_markup = $this->restaurant()->delivery_service_markup ? $this->restaurant()->delivery_service_markup : 0;
|
|
$price_delivery_markup = number_format($subtotal * $delivery_service_markup / 100, 2);
|
|
$subtotal_plus_delivery_service_markup = $subtotal + $price_delivery_markup;
|
|
$this->type = 'restaurant';
|
|
|
|
} else {
|
|
|
|
foreach ($params['cart'] as $d) {
|
|
$dish = new Order_Dish;
|
|
$dish->id_dish = $d['id'];
|
|
$price = number_format( $dish->dish()->price, 2);
|
|
$price_delivery_markup = $price;
|
|
if( $delivery_service_markup ){
|
|
$price_delivery_markup = $price_delivery_markup + ( $price_delivery_markup * $delivery_service_markup / 100 );
|
|
$price_delivery_markup = Crunchbutton_Restaurant::roundDeliveryMarkupPrice( $price_delivery_markup );
|
|
}
|
|
|
|
$_dish = Dish::o( $dish->id_dish );
|
|
if( $_dish->id_restaurant != $this->id_restaurant ){
|
|
$food_error = true;
|
|
$foodErrorDishes[] = $dish->id_dish;
|
|
}
|
|
|
|
$subtotal += number_format( $price, 2 );
|
|
$subtotal_plus_delivery_service_markup += number_format( $price_delivery_markup, 2 );
|
|
if ($d['options']) {
|
|
foreach ($d['options'] as $o) {
|
|
$option = new Order_Dish_Option;
|
|
$option->id_option = $o;
|
|
$price = $option->option()->price;
|
|
$price_delivery_markup = $price;
|
|
if( $delivery_service_markup ){
|
|
$price_delivery_markup = $price_delivery_markup + ( $price_delivery_markup * $delivery_service_markup / 100 );
|
|
$price_delivery_markup = Crunchbutton_Restaurant::roundDeliveryMarkupPrice( $price_delivery_markup );
|
|
}
|
|
$subtotal_plus_delivery_service_markup += number_format( $price_delivery_markup, 2 );
|
|
$subtotal += number_format( $price, 2 );
|
|
$dish->_options[] = $option;
|
|
}
|
|
}
|
|
$this->_dishes[] = $dish;
|
|
}
|
|
$this->type = 'web';
|
|
}
|
|
|
|
// to make sure the value will be 2 decimals
|
|
$this->delivery_service_markup_value = number_format( $subtotal_plus_delivery_service_markup - $subtotal, 2 );
|
|
|
|
$this->_card = $params['card'];
|
|
|
|
// price and price_plus_delivery_markup #2236
|
|
$this->price = Util::ceil( $subtotal, 2 );
|
|
$this->price_plus_delivery_markup = Util::ceil( $subtotal_plus_delivery_service_markup, 2 );
|
|
|
|
// delivery fee
|
|
$this->delivery_fee = ($this->restaurant()->delivery_fee && $this->delivery_type == 'delivery') ? $this->restaurant()->delivery_fee : 0;
|
|
|
|
// service fee for customer
|
|
$this->service_fee = $this->restaurant()->fee_customer;
|
|
$serviceFee = ($this->price + $this->delivery_fee) * Util::ceil(($this->service_fee/100),2);
|
|
|
|
if( $this->campus_cash && $this->campusCashFee() ){
|
|
$serviceFee += number_format(($this->price + $this->delivery_fee) * ($this->campusCashFee()/100),2);
|
|
}
|
|
|
|
$serviceFee = Util::ceil( $serviceFee, 2);
|
|
$totalWithFees = $this->price + $this->delivery_fee + $serviceFee;
|
|
$totalWithFees = Util::ceil( $totalWithFees, 2);
|
|
|
|
// Start to store the fee_restaurant because it could change and we need to know the
|
|
// exacly value at the moment the user ordered his food
|
|
$this->fee_restaurant = $this->restaurant()->fee_restaurant;
|
|
|
|
// tip
|
|
$this->tip = $params['tip'];
|
|
if(!strcmp($this->tip, 'autotip')) {
|
|
$this->tip = floatval($params['autotip_value']);
|
|
$tip = $this->tip;
|
|
$tip = Util::ceil($tip, 2);
|
|
$this->tip_type = static::TIP_NUMBER;
|
|
}
|
|
else {
|
|
// tip - percent
|
|
/* - to calculate the tip it must use as reference the price with mark up
|
|
because the marked up price was the one shown the the user
|
|
- see talk between pererinha and david at hipchat 02/17/2014
|
|
https://github.com/crunchbutton/crunchbutton/issues/2248#issuecomment-35381055 */
|
|
$tip = ( $this->price_plus_delivery_markup * ( $this->tip / 100 ) );
|
|
$tip = Util::ceil( $tip, 2 );
|
|
$this->tip_type = static::TIP_PERCENT;
|
|
}
|
|
|
|
// tax
|
|
/* - taxes should be calculated using the price without markup
|
|
- if restaurant uses 3rd party delivery service remove the delivery_fee
|
|
- see #2236 and #2248
|
|
-> Removed the Util::ceil - see #2613
|
|
*/
|
|
if($this->restaurant()->delivery_service){
|
|
$baseToCalcTax = $this->price;
|
|
} else {
|
|
$baseToCalcTax = $this->price + $this->delivery_fee;
|
|
}
|
|
|
|
$this->tax = $this->restaurant()->tax;
|
|
$tax = $baseToCalcTax * ( $this->tax / 100 );
|
|
$tax = number_format( round( $tax, 2 ), 2 );
|
|
|
|
$cb_service_fee = $baseToCalcTax * ( $this->restaurant()->service_fee / 100 );
|
|
$this->cb_service_fee = number_format( $cb_service_fee, 2 );
|
|
|
|
if($this->restaurant()->delivery_service){
|
|
$this->final_price = Util::ceil( $totalWithFees + $tax + $this->cb_service_fee, 2 ); // price
|
|
$this->final_price_plus_delivery_markup = Util::ceil( $this->final_price + $this->delivery_service_markup_value + $tip, 2 );
|
|
} else {
|
|
$this->final_price = Util::ceil( $totalWithFees + $tip + $tax + $this->cb_service_fee, 2 ); // price
|
|
$this->final_price_plus_delivery_markup = Util::ceil( $this->final_price + $this->delivery_service_markup_value, 2 );
|
|
}
|
|
|
|
$this->order = json_encode($params['cart']);
|
|
|
|
if (!c::user()->id_user) {
|
|
if (!$params['name']) {
|
|
$errors['name'] = 'Please enter a name.';
|
|
}
|
|
if (!$params['phone']) {
|
|
$errors['phone'] = 'Please enter a phone #.';
|
|
}
|
|
if (!$params['address'] && $this->delivery_type == 'delivery') {
|
|
$errors['address'] = 'Please enter an address.';
|
|
}
|
|
} else {
|
|
if (!$params['address']) {
|
|
$this->address = c::user()->address;
|
|
}
|
|
if (!$params['phone']) {
|
|
$this->phone = c::user()->phone;
|
|
}
|
|
if (!$params['name']) {
|
|
$this->name = c::user()->name;
|
|
}
|
|
}
|
|
|
|
// Block orders from this "customer" #6136
|
|
if( c::user()->id_user ){
|
|
if( Crunchbutton_Blocked::isUserBlocked( c::user()->id_user ) ){
|
|
$errors['error'] = Crunchbutton_Blocked::getMessage();
|
|
}
|
|
}
|
|
if( $this->phone ){
|
|
if( Crunchbutton_Blocked::isPhoneNumberBlocked( $this->phone ) ){
|
|
$errors['error'] = Crunchbutton_Blocked::getMessage();
|
|
}
|
|
}
|
|
|
|
if (!$this->restaurant()->open() && !$this->preordered ) {
|
|
$errors['closed'] = 'This restaurant is closed.';
|
|
|
|
$time = new DateTime('now', new DateTimeZone($this->restaurant()->timezone));
|
|
$debug = [
|
|
'type' => 'closed',
|
|
'time' => $time->format('Y-m-d H:i:s'),
|
|
'timezone' => $this->restaurant()->timezone,
|
|
'cart' => $params['cart'],
|
|
'params' => $params,
|
|
'restaurant' => $this->restaurant()->exports(),
|
|
];
|
|
Crunchbutton_Log::error($debug);
|
|
|
|
if (Cana::env() != 'live') {
|
|
$errors['debug'] = $debug;
|
|
}
|
|
}
|
|
|
|
if (!$this->restaurant()->meetDeliveryMin($this) && $this->delivery_type == 'delivery') {
|
|
$errors['minimum'] = 'Please meet the delivery minimum of '.$this->restaurant()->delivery_min.'.';
|
|
}
|
|
|
|
// check if the restaurant accepts campus cash payment method
|
|
if( $this->campus_cash ){
|
|
if( $this->restaurant()->campusCash() ){
|
|
if( $this->campus_cash && !$params[ 'campusCash' ] ){
|
|
$errors['campusCash'] = 'Please fill the field '.$this->restaurant()->campusCashName().'.';
|
|
} else {
|
|
if( $this->campus_cash ){
|
|
$this->campusCash = $this->restaurant()->campusCashValidate( $params[ 'campusCash' ] );
|
|
}
|
|
|
|
if( $this->campus_cash && !$this->campusCash ){
|
|
$errors['campusCashInvalid'] = 'Please enter a valid '.$this->restaurant()->campusCashName().'.';
|
|
}
|
|
}
|
|
} else {
|
|
$errors['payment_method'] = 'Please select a valid payment method.';
|
|
}
|
|
}
|
|
|
|
// Check if the food belongs to the restaurant - #7404
|
|
if ( $food_error ) {
|
|
$errors['food'] = 'There was an error processing your order. Please reload the page and tray again.';
|
|
$errors['foodDishes'] = $foodErrorDishes;
|
|
}
|
|
|
|
if ($errors) {
|
|
// Log the order - validation error
|
|
Log::debug([
|
|
'action' => 'validation error',
|
|
'address' => $params['address'],
|
|
'phone' => $params['phone'],
|
|
'pay_type' => $params['pay_type'],
|
|
'tip' => $params['tip'],
|
|
'autotip' => $params['autotip'],
|
|
'autotip_value' => $params['autotip_value'],
|
|
'name' => $params['name'],
|
|
'user_id' => c::user()->id_user,
|
|
'delivery_type' => $params['delivery_type'],
|
|
'restaurant' => $params['restaurant'],
|
|
'notes' => $params['notes'],
|
|
'errors' => $params['errors'],
|
|
'cart' => $params['cart'],
|
|
'type' => 'order-log'
|
|
]);
|
|
return $errors;
|
|
}
|
|
|
|
$user = c::user()->id_user ? c::user() : new User;
|
|
|
|
if (!c::user()->id_user) {
|
|
$user->active = 1;
|
|
}
|
|
|
|
// Save the user just to add to him the gift cards
|
|
$user->saving_from = $user->saving_from.'Order->process 1 - ';
|
|
|
|
$user->save();
|
|
|
|
// Reload the user from db #1737
|
|
$user = new User(c::dbWrite()->get('select * from `user` where id_user=?', [$user->id_user])->get(0));
|
|
|
|
$this->id_user = $user->id_user;
|
|
|
|
$this->giftCardInviter = false;
|
|
|
|
// Find out if the user posted a gift card code at the notes field and get its value
|
|
$this->giftcardValue = 0;
|
|
if ( trim( $this->notes ) != '' ){
|
|
$totalOrdersByPhone = self::totalOrdersByPhone( $this->phone );
|
|
if( $totalOrdersByPhone < 1 ){
|
|
$words = preg_replace( "/(\r\n|\r|\n)+/", ' ', $this->notes );
|
|
$words = explode( ' ', $words );
|
|
$words = array_unique( $words );
|
|
foreach( $words as $word ){
|
|
$giftCardAdded = false;
|
|
// At first check if it is an user's invite code - rewards: two way gift cards #2561
|
|
$reward = new Crunchbutton_Reward;
|
|
$add_points = $reward->getRefered();
|
|
$delivery_free_points = $reward->pointsToGetDeliveryFree();
|
|
$inviter = $reward->validateInviteCode( $word );
|
|
if( $totalOrdersByPhone <= 1 && $inviter ){
|
|
// get the value of the discount
|
|
if( $inviter[ 'id_admin' ] ){
|
|
|
|
if( $add_points == $delivery_free_points ){
|
|
$value = $this->restaurant()->delivery_fee;
|
|
$admin_credit = $reward->adminRefersNewUserCreditAmount();
|
|
$this->giftCardInviter = [ 'id_user' => $inviter[ 'id_user' ], 'id_admin' => $inviter[ 'id_admin' ], 'value' => $value, 'word' => $word, 'admin_credit' => $admin_credit ];
|
|
if( $value ){
|
|
$this->giftcardValue = $value;
|
|
break;
|
|
}
|
|
} else {
|
|
$value = $reward->getReferredDiscountAmount();
|
|
$admin_credit = $reward->adminRefersNewUserCreditAmount();
|
|
$this->giftCardInviter = [ 'id_user' => $inviter[ 'id_user' ], 'id_admin' => $inviter[ 'id_admin' ], 'value' => $value, 'word' => $word, 'admin_credit' => $admin_credit ];
|
|
if( $value ){
|
|
$this->giftcardValue = $value;
|
|
break;
|
|
}
|
|
}
|
|
} elseif( $inviter[ 'id_user' ] ){
|
|
$referral = new Crunchbutton_Referral();
|
|
$referral->id_admin_inviter = null;
|
|
$referral->id_user_inviter = $inviter[ 'id_user'];
|
|
$referral->id_user_invited = $this->id_user;
|
|
$referral->admin_credit = null;
|
|
$referral->invite_code = $word;
|
|
$referral->new_user = 1;
|
|
$referral->date = date('Y-m-d H:i:s');
|
|
$referral->save();
|
|
|
|
$settings = $reward->loadSettings();
|
|
// if the amount of points is the same points that gives the customer a delivery free
|
|
// it just add credit to customer
|
|
if( $add_points == $delivery_free_points ){
|
|
$credits_amount = $this->restaurant()->delivery_fee;
|
|
if( $credits_amount ){
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $this->id_user,
|
|
'value' => $credits_amount,
|
|
'id_order' => null,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_CASH,
|
|
'id_referral' => $referral->id_referral,
|
|
'note' => 'Cash Invited by: ' . $inviter[ 'id_user'] . ' code: ' . $word,
|
|
] );
|
|
}
|
|
} else {
|
|
$credits_amount = $settings[ Crunchbutton_Reward::CONFIG_KEY_GET_REFERRED_DISCOUNT_AMOUNT ];
|
|
if( $credits_amount ){
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $this->id_user,
|
|
'value' => $credits_amount,
|
|
'id_order' => null,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_CASH,
|
|
'id_referral' => $referral->id_referral,
|
|
'note' => 'Cash Invited by: ' . $inviter[ 'id_user'] . ' code: ' . $word,
|
|
] );
|
|
}
|
|
|
|
$credits_amount = $settings[ Crunchbutton_Reward::CONFIG_KEY_GET_REFERRED_VALUE ];
|
|
if( $credits_amount ){
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $this->id_user,
|
|
'value' => $credits_amount,
|
|
'id_order' => null,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_POINT,
|
|
'id_referral' => $referral->id_referral,
|
|
'note' => 'Points Invited by: ' . $inviter[ 'id_user'] . ' code: ' . $word,
|
|
] );
|
|
}
|
|
}
|
|
|
|
|
|
$credits_amount = $settings[ Crunchbutton_Reward::CONFIG_KEY_REFER_NEW_USER_AMOUNT ];
|
|
if( $credits_amount ){
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $inviter[ 'id_user'],
|
|
'value' => $credits_amount,
|
|
'id_order' => null,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_CASH,
|
|
'id_referral' => $referral->id_referral,
|
|
'note' => 'Cash Invited ID: ' . $this->id_user . ' code: ' . $word,
|
|
] );
|
|
}
|
|
|
|
$credits_amount = $settings[ Crunchbutton_Reward::CONFIG_KEY_REFER_NEW_USER_VALUE ];
|
|
if( $credits_amount ){
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $inviter[ 'id_user'],
|
|
'value' => $credits_amount,
|
|
'id_order' => null,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_POINT,
|
|
'id_referral' => $referral->id_referral,
|
|
'note' => 'Points Invited ID: ' . $this->id_user . ' code: ' . $word,
|
|
] );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
// if the code doesn't belong to a user check if it belongs to a gift card
|
|
if( !$this->giftCardInviter ) {
|
|
|
|
$giftcards = Crunchbutton_Promo::validateNotesField( $this->notes, $this->id_restaurant, $this->phone );
|
|
foreach ( $giftcards[ 'giftcards' ] as $giftcard ) {
|
|
if( $giftcard->id_promo ){
|
|
if( !$giftCardAdded ){
|
|
$this->giftcardValue = $giftcard->value;
|
|
$giftCardAdded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$_notes = $giftcards[ 'notes' ];
|
|
}
|
|
|
|
Log::debug([
|
|
'issue' => '#1551',
|
|
'method' => 'process',
|
|
'$this->final_price' => $this->final_price,
|
|
'giftcardValue'=> $this->giftcardValue,
|
|
'$_notes' => $_notes,
|
|
'$this->notes' => $this->notes
|
|
]);
|
|
|
|
$delivery_free = false;
|
|
if( $params['use_delivery_points'] ){
|
|
// Point redemption system improvements for customer-customer referrals #4248
|
|
// https://github.com/crunchbutton/crunchbutton/issues/5092#issuecomment-90966989
|
|
// of the same amount of the delivery order
|
|
if ( $this->restaurant()->hasDeliveryService() && $this->delivery_type == 'delivery' && $this->delivery_fee && $this->pay_type == 'card' ) {
|
|
$reward = new Crunchbutton_Reward;
|
|
$reward = $reward->loadSettings();
|
|
$user_points = Crunchbutton_Credit::points( $user->id_user );
|
|
if( $user_points >= intval( $reward[ Crunchbutton_Reward::CONFIG_KEY_MAX_CAP_POINTS ] ) ){
|
|
$delivery_free = true;
|
|
// Add credit
|
|
|
|
$credit_amount = $this->delivery_fee;
|
|
|
|
if( $serviceFee ){
|
|
$credit_amount += $serviceFee;
|
|
}
|
|
|
|
$credit = new Crunchbutton_Credit();
|
|
$credit->id_user = $this->id_user;
|
|
$credit->type = Crunchbutton_Credit::TYPE_CREDIT;
|
|
$credit->id_restaurant = $this->id_restaurant;
|
|
$credit->id_promo = null;
|
|
$credit->date = date('Y-m-d H:i:s');
|
|
$credit->value = $credit_amount;
|
|
$credit->credit_type = Crunchbutton_Credit::CREDIT_TYPE_CASH;
|
|
$credit->paid_by = 'CRUNCHBUTTON';
|
|
$credit->note = 'Reward: delivery free';
|
|
$credit->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
// process the payment
|
|
$res = $this->verifyPayment();
|
|
|
|
// failed to process the card
|
|
if ($res !== true) {
|
|
Log::error( [
|
|
'action' => 'credit card error',
|
|
'address' => $params['address'],
|
|
'phone' => $params['phone'],
|
|
'pay_type' => $params['pay_type'],
|
|
'tip' => $params['tip'],
|
|
'autotip' => $params['autotip'],
|
|
'autotip_value' => $params['autotip_value'],
|
|
'name' => $params['name'],
|
|
'user_id' => c::user()->id_user,
|
|
'delivery_type' => $params['delivery_type'],
|
|
'restaurant' => $params['restaurant'],
|
|
'notes' => $params['notes'],
|
|
'errors' => $res['errors'],
|
|
'geomatched' => $params['geomatched'],
|
|
'cart' => $params['cart'],
|
|
'type' => 'order-log'
|
|
] );
|
|
|
|
// Remove the delivery free credit
|
|
if( $delivery_free ){
|
|
|
|
$credits_amount = $this->delivery_fee;
|
|
if( $serviceFee ){
|
|
$credits_amount += $serviceFee;
|
|
}
|
|
|
|
$credit = new Crunchbutton_Credit();
|
|
$credit->id_user = $this->id_user;
|
|
$credit->type = Crunchbutton_Credit::TYPE_DEBIT;
|
|
$credit->id_restaurant = $this->id_restaurant;
|
|
$credit->id_promo = null;
|
|
$credit->date = date('Y-m-d H:i:s');
|
|
$credit->value = $credits_amount;
|
|
$credit->credit_type = Crunchbutton_Credit::CREDIT_TYPE_CASH;
|
|
$credit->paid_by = 'CRUNCHBUTTON';
|
|
$credit->note = 'Reward: removed delivery free cash';
|
|
$credit->save();
|
|
}
|
|
|
|
return $res['errors'];
|
|
|
|
// successfully processed the card
|
|
} else {
|
|
$this->txn = $this->transaction();
|
|
}
|
|
|
|
// Remove the user points if they were used to get free delivery
|
|
if( $delivery_free ){
|
|
$free_delivery_points = intval( $reward[ Crunchbutton_Reward::CONFIG_KEY_MAX_CAP_POINTS ] );
|
|
$credit = new Crunchbutton_Credit();
|
|
$credit->id_user = $this->id_user;
|
|
$credit->type = Crunchbutton_Credit::TYPE_DEBIT;
|
|
$credit->id_restaurant = null;
|
|
$credit->id_promo = null;
|
|
$credit->date = date('Y-m-d H:i:s');
|
|
$credit->value = $free_delivery_points;
|
|
$credit->credit_type = Crunchbutton_Credit::CREDIT_TYPE_POINT;
|
|
$credit->paid_by = 'CRUNCHBUTTON';
|
|
$credit->note = 'Reward: removed delivery free points';
|
|
$this->reward_delivery_free = 1;
|
|
$credit->save();
|
|
}
|
|
|
|
$user->location_lat = $params['lat'];
|
|
$user->location_lon = $params['lon'];
|
|
|
|
$this->location_lat = $params['lat'];
|
|
$this->location_lon = $params['lon'];
|
|
|
|
$user->name = $this->name;
|
|
$user->email = $params['email'];
|
|
$user->phone = $this->phone;
|
|
|
|
$phone = Crunchbutton_Phone::byPhone( $this->phone );
|
|
$_address = Crunchbutton_Address::byAddress( $this->address );
|
|
|
|
$this->id_address = $_address->id_address;
|
|
|
|
$user->id_phone = $phone->id_phone;
|
|
|
|
if ($this->delivery_type == 'delivery') {
|
|
$user->address = $this->address;
|
|
}
|
|
|
|
$user->pay_type = $this->pay_type;
|
|
$user->delivery_type = $this->delivery_type;
|
|
$user->tip = $this->tip;
|
|
|
|
$this->env = c::getEnv(false);
|
|
$this->processor = Crunchbutton_User_Payment_Type::processor();
|
|
|
|
$user->saving_from = $user->saving_from.'Order->process 2 - ';
|
|
$user->save();
|
|
|
|
$user = new User(c::dbWrite()->get('select * from `user` where id_user=?', [$user->id_user])->get(0));
|
|
$this->_user = $user;
|
|
|
|
// If the pay_type is card
|
|
if ($this->pay_type == 'card' ) {
|
|
// Verify if the user already has a payment type
|
|
$payment_type = $user->payment_type();
|
|
|
|
// if the user gave us a new card, store it because thats the one we used
|
|
if (!$payment_type || $this->_card['id'] || $this->campus_cash) {
|
|
|
|
if( $this->campus_cash && $this->_campus_cash_sha1 ){
|
|
|
|
$last_digits = substr( $this->campusCash, -4 );
|
|
|
|
// create a new payment type
|
|
$payment_type = new User_Payment_Type([
|
|
'id_user' => $user->id_user,
|
|
'active' => 1,
|
|
'card' => $last_digits,
|
|
'card_type' => Crunchbutton_User_Payment_Type::CARD_TYPE_CAMPUS_CASH,
|
|
'stripe_id' => $this->_campus_cash_sha1,
|
|
'stripe_customer' => $this->_customer,
|
|
'card_exp_year' => null,
|
|
'card_exp_month' => null,
|
|
'date' => date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
$payment_type->save();
|
|
|
|
// Desactive others payments
|
|
$payment_type->desactiveOlderPaymentsType();
|
|
|
|
} else {
|
|
// create a new payment type
|
|
$payment_type = new User_Payment_Type([
|
|
'id_user' => $user->id_user,
|
|
'active' => 1,
|
|
'card' => $this->_card['lastfour'] ? ('************'.$this->_card['lastfour']) : '',
|
|
'card_type' => $this->_card['card_type'],
|
|
'card_exp_year' => $this->_card['year'],
|
|
'card_exp_month' => $this->_card['month'],
|
|
'date' => date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
switch (Crunchbutton_User_Payment_Type::processor()) {
|
|
case 'stripe':
|
|
$payment_type->stripe_id = $this->_paymentType;
|
|
$user->stripe_id = $this->_customer;
|
|
$user->save();
|
|
break;
|
|
}
|
|
|
|
$payment_type->save();
|
|
|
|
// Desactive others payments
|
|
$payment_type->desactiveOlderPaymentsType();
|
|
}
|
|
}
|
|
$this->id_user_payment_type = $payment_type->id_user_payment_type;
|
|
}
|
|
|
|
// If the user typed a password it will create a new user auth
|
|
if( $params['password'] != '' ){
|
|
$params_auth = array();
|
|
$params_auth[ 'email' ] = $params['phone'];
|
|
$params_auth[ 'password' ] = $params[ 'password' ];
|
|
$emailExists = User_Auth::checkEmailExists( $params_auth[ 'email' ] );
|
|
if( !$emailExists ){
|
|
$user_auth = new User_Auth();
|
|
$user_auth->id_user = $user->id_user;
|
|
$user_auth->type = 'local';
|
|
$user_auth->auth = User_Auth::passwordEncrypt( $params_auth[ 'password' ] );
|
|
$user_auth->email = $params_auth[ 'email' ];
|
|
$user_auth->active = 1;
|
|
$user_auth->save();
|
|
}
|
|
}
|
|
|
|
// This line will create a phone user auth just if the user already has an email auth
|
|
User_Auth::createPhoneAuth( $user->id_user, $user->phone );
|
|
User_Auth::createPhoneAuthFromFacebook( $user->id_user, $user->phone );
|
|
|
|
// we used to generate a $this->_customer->email_address using a uuid here for some reason
|
|
|
|
if ($processType != static::PROCESS_TYPE_RESTAURANT) {
|
|
c::auth()->session()->adapter()->id_user = $user->id_user;
|
|
c::auth()->session()->generateAndSaveToken();
|
|
}
|
|
|
|
$agent = Crunchbutton_Agent::getAgent();
|
|
$this->id_agent = $agent->id_agent;
|
|
|
|
if (c::auth()->session()->adapter()->id_session != '') {
|
|
$this->id_session = c::auth()->session()->adapter()->id_session;
|
|
}
|
|
|
|
$this->id_user = $this->_user->id_user;
|
|
if( $this->preordered ){
|
|
$this->preordered_date = date('Y-m-d H:i:s');
|
|
} else {
|
|
$this->date = date('Y-m-d H:i:s');
|
|
}
|
|
|
|
$this->delivery_service = $this->restaurant()->hasDeliveryService();
|
|
$this->id_community = $this->restaurant()->community()->id_community;
|
|
|
|
$this->geomatched = ( intval( $params['geomatched'] ) ? 1 : 0 );
|
|
|
|
$phone = Crunchbutton_Phone::byPhone( $this->phone );
|
|
$this->id_phone = $phone->id_phone;
|
|
|
|
// Get the informed eta before save the order
|
|
if( $this->restaurant() ){
|
|
$informed_eta = $this->restaurant()->smartETA();
|
|
}
|
|
|
|
$this->save();
|
|
|
|
// register informed eta
|
|
if( $informed_eta ){
|
|
$eta = new Order_Eta([
|
|
'id_order' => $this->id_order,
|
|
'time' => $informed_eta,
|
|
'distance' => null,
|
|
'date' => date('Y-m-d H:i:s'),
|
|
'method' => Crunchbutton_Order_Eta::METHOD_INFORMED_ETA
|
|
]);
|
|
$eta->save();
|
|
}
|
|
|
|
Log::debug( [ '$this->giftCardInviter' => $this->giftCardInviter, '$this->notes' => $this->notes ] );
|
|
|
|
// If the payment succeds then redeem the gift card
|
|
if( $this->giftCardInviter ){
|
|
|
|
// remove the code from notes
|
|
$__order = Order::o( $this->id_order );
|
|
$__order->notes = str_replace( $this->giftCardInviter[ 'word' ], '', $__order->notes );
|
|
$__order->save();
|
|
|
|
$referral = new Crunchbutton_Referral();
|
|
$referral->id_admin_inviter = $this->giftCardInviter[ 'id_admin'];
|
|
$referral->id_user_inviter = $this->giftCardInviter[ 'id_user'];
|
|
$referral->id_user_invited = $this->id_user;
|
|
$referral->id_order = $this->id_order;
|
|
$referral->admin_credit = $this->giftCardInviter[ 'admin_credit'];
|
|
$referral->invite_code = $this->giftCardInviter[ 'word'];
|
|
$referral->new_user = 1;
|
|
$referral->date = date('Y-m-d H:i:s');
|
|
$referral->save();
|
|
|
|
$reward = new Crunchbutton_Reward;
|
|
// the new user earns discount
|
|
if( $this->giftCardInviter[ 'id_admin'] ){
|
|
$notes = 'Inviter ID: ' . $this->giftCardInviter[ 'id_admin'] . ' code: ' . $this->giftCardInviter[ 'word'];
|
|
}
|
|
|
|
$reward->saveRewardAsCredit( [ 'id_user' => $user->id_user,
|
|
'value' => $this->giftCardInviter[ 'value'],
|
|
'id_order' => $this->id_order,
|
|
'id_referral' => $referral->id_referral,
|
|
'credit_type' => Crunchbutton_Credit::CREDIT_TYPE_CASH,
|
|
'note' => $notes,
|
|
] );
|
|
|
|
|
|
if( $this->giftCardInviter[ 'id_admin'] ){
|
|
$credits_amount = $this->giftCardInviter[ 'admin_credit'];
|
|
Log::debug([ 'id_admin' => $this->giftCardInviter[ 'id_admin'], '$credits_amount' => $credits_amount ]);
|
|
}
|
|
} else {
|
|
if( $this->notes ){
|
|
$giftcards = Crunchbutton_Promo::validateNotesField( $this->notes, $this->id_restaurant, $this->phone );
|
|
$giftCardAdded = false;
|
|
foreach ( $giftcards[ 'giftcards' ] as $giftcard ) {
|
|
if( $giftcard->id_promo ){
|
|
if( !$giftCardAdded ){
|
|
$giftcard->addCredit( $user->id_user, $this->delivery_fee );
|
|
}
|
|
$giftCardAdded = true;
|
|
}
|
|
}
|
|
$_order = Order::o( $this->id_order );
|
|
$_order->notes = $giftcards[ 'notes' ];
|
|
$_order->save();
|
|
$this->notes = $_order->notes;
|
|
}
|
|
}
|
|
|
|
$this->debitFromUserCredit( $user->id_user );
|
|
|
|
if ( $params['make_default'] == 'true' ) {
|
|
$preset = $user->preset($this->restaurant()->id_restaurant);
|
|
if ($preset->id_preset) {
|
|
$preset->delete();
|
|
}
|
|
}
|
|
|
|
if ($this->_dishes) {
|
|
foreach ($this->_dishes as $dish) {
|
|
$dish->id_order = $this->id_order;
|
|
$dish->save();
|
|
$_Dish = Dish::o( $dish->id_dish );
|
|
foreach ($dish->options() as $option) {
|
|
# Issue 1437 - https://github.com/crunchbutton/crunchbutton/issues/1437#issuecomment-20561023
|
|
# 1 - When an option is removed, it should NEVER appear in the order or on the fax.
|
|
if( $_Dish->dish_has_option( $option->id_option ) ){
|
|
$option->id_order_dish = $dish->id_order_dish;
|
|
$option->save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !$this->preordered ){
|
|
$this->que();
|
|
} else {
|
|
// send customer a receipt in 30 seconds
|
|
$q = Queue::create([
|
|
'type' => Crunchbutton_Queue::TYPE_ORDER_RECEIPT,
|
|
'id_order' => $this->id_order,
|
|
'seconds' => 30
|
|
]);
|
|
}
|
|
|
|
$order = $this;
|
|
|
|
if ( $params['make_default'] == 'true' ) {
|
|
Preset::cloneFromOrder($order);
|
|
}
|
|
|
|
if ($processType != static::PROCESS_TYPE_RESTAURANT) {
|
|
|
|
// Reward
|
|
$reward = new Crunchbutton_Reward;
|
|
// rewards: earn points when an order has been placed #2458
|
|
$points = $reward->processOrder( $order->id_order );
|
|
if( floatval( $points ) > 0 ){
|
|
if( User_Auth::userHasAuth( $order->id_user ) ){
|
|
$reward->saveReward( [ 'id_order' => $order->id_order, 'id_user' => $order->id_user, 'points' => $points, 'note' => 'points by order #' . $order->id_order ] );
|
|
}
|
|
}
|
|
// rewards: 4x points when ordering 2 days in a row #3434
|
|
$points = $reward->orderTwoDaysInARow( $order->id_user );
|
|
if( floatval( $points ) > 0 ){
|
|
if( User_Auth::userHasAuth( $order->id_user ) ){
|
|
$reward->saveReward( [ 'id_order' => $order->id_order, 'id_user' => $order->id_user, 'points' => $points, 'note' => 'points by 2 days in a row' ] );
|
|
}
|
|
}
|
|
if( !$points ){
|
|
// rewards: 2x points when ordering in same week #3432
|
|
$points = $reward->orderTwiceSameWeek( $order->id_user );
|
|
if( floatval( $points ) > 0 ){
|
|
if( User_Auth::userHasAuth( $order->id_user ) ){
|
|
$reward->saveReward( [ 'id_order' => $order->id_order, 'id_user' => $order->id_user, 'points' => $points, 'note' => 'points by ordering twice same week' ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->removeCouponCodesInTheNotes();
|
|
|
|
Crunchbutton_Order_Data::register( $this );
|
|
|
|
$this->createTicketForNotGeomatchedOrder();
|
|
|
|
return true;
|
|
}
|
|
|
|
public function minutesToBeDelivered(){
|
|
if( $this->date_delivery ){
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$date_delivery = new DateTime( $this->date_delivery, new DateTimeZone( c::config()->timezone ) );
|
|
if( $now < $date_delivery ){
|
|
$seconds = Util::intervalToSeconds( $now->diff( $date_delivery ) );
|
|
if( $seconds > 0 ){
|
|
$minutes = ( $seconds / 60 );
|
|
return $minutes;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function removeCouponCodesInTheNotes(){
|
|
// fix for #4256
|
|
$_order = Crunchbutton_Order::o( $this->id_order );
|
|
if ( trim( $_order->notes ) != '' ){
|
|
$words = str_replace( "\n", ' ', $this->notes );
|
|
$words = explode( ' ', $words );
|
|
$words = array_unique( $words );
|
|
$reward = new Crunchbutton_Reward;
|
|
foreach( $words as $word ){
|
|
$inviter = $reward->validateInviteCode( $word );
|
|
if( $inviter ){
|
|
$_order->notes = str_replace( $word, '', $_order->notes );
|
|
}
|
|
}
|
|
$_order->save();
|
|
}
|
|
}
|
|
|
|
public function campusCashName(){
|
|
return ( $this->restaurant()->campusCashName() ? $this->restaurant()->campusCashName() : 'Student ID Number' );
|
|
}
|
|
|
|
public function campusCashReceiptInfo(){
|
|
return $this->restaurant()->campusCashReceiptInfo();
|
|
}
|
|
|
|
public function campusCashLastDigits(){
|
|
$paymentType = $this->paymentType();
|
|
return $paymentType->card;
|
|
}
|
|
|
|
public function campusCashFee(){
|
|
return $this->restaurant()->campusCashFee();
|
|
}
|
|
|
|
public function signature(){
|
|
return Crunchbutton_Order_Signature::retrieve( $this->id_order );
|
|
}
|
|
|
|
public function requireSignature(){
|
|
if( $this->campus_cash ){
|
|
$community = $this->community();
|
|
if( $community->id_community && $community->requireSignature() ){
|
|
$signature = ( $this->signature() ? true : false );
|
|
if( $signature ){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function calcFinalPriceMinusUsersCredit(){
|
|
$final_price = $this->final_price_plus_delivery_markup;
|
|
if( $this->pay_type == 'card' ){
|
|
$final_price = $final_price - $this->giftcardValue;
|
|
if( $this->id_user ){
|
|
$chargedByCredit = Crunchbutton_Credit::calcDebitFromUserCredit( $final_price, $this->id_user, $this->id_restaurant, $this->id_order, true );
|
|
$final_price = $final_price - $chargedByCredit;
|
|
}
|
|
Log::debug([ 'issue' => '#1551', 'method' => 'calcFinalPriceMinusUsersCredit', 'final_price_plus_delivery_markup' => $this->final_price_plus_delivery_markup, 'final_price' => $this->final_price, 'giftcardValue'=> $this->giftcardValue, 'delivery_service_markup' => $this->delivery_service_markup ]);
|
|
if( $final_price < 0 ){ $final_price = 0; }
|
|
return Util::ceil( $final_price, 2 );
|
|
}
|
|
return $final_price;
|
|
}
|
|
|
|
public function chargedByCredit(){
|
|
$totalCredit = 0;
|
|
if( $this->pay_type == 'card' ){
|
|
$credits = Crunchbutton_Credit::creditByOrder( $this->id_order );
|
|
if( $credits->count() > 0 ){
|
|
foreach( $credits as $credit ){
|
|
$totalCredit = $totalCredit + $credit->value;
|
|
}
|
|
}
|
|
}
|
|
return number_format( $totalCredit, 2 );
|
|
}
|
|
|
|
public function charged(){
|
|
|
|
if( $this->final_price_plus_delivery_markup && floatval( $this->final_price_plus_delivery_markup ) > 0 ){
|
|
$final_price = $this->final_price_plus_delivery_markup;
|
|
} else {
|
|
$final_price = $this->final_price;
|
|
}
|
|
return number_format( abs( ( $final_price ) - ( $this->chargedByCredit() ) ), 2 );
|
|
}
|
|
|
|
public function debitFromUserCredit( $id_user ){
|
|
if( $this->pay_type == 'card' ){
|
|
$final_price = $this->final_price_plus_delivery_markup;
|
|
Crunchbutton_Credit::debitFromUserCredit( $final_price, $id_user, $this->id_restaurant, $this->id_order );
|
|
}
|
|
}
|
|
|
|
public static function uuid($uuid) {
|
|
return self::q('select * from `order` where uuid=?', [$uuid]);
|
|
}
|
|
|
|
// return an order based on its local_gid - see #3086
|
|
public static function gid( $gid ) {
|
|
return self::q( 'SELECT * FROM `order` WHERE local_gid=?', [$gid]);
|
|
}
|
|
|
|
/**
|
|
* The restaurant to process the order
|
|
*
|
|
* @return Crunchbutton_Restaurant
|
|
*/
|
|
public function restaurant() {
|
|
if( !$this->_restaurant ){
|
|
$this->_restaurant = Restaurant::o($this->id_restaurant);
|
|
}
|
|
return $this->_restaurant;
|
|
}
|
|
|
|
public function user() {
|
|
return User::o($this->id_user);
|
|
}
|
|
|
|
public function accepted() {
|
|
$nl = Notification_Log::q("select * from notification_log where id_order=? and status='accepted'", [$this->id_order]);
|
|
return $nl->count() ? true : false;
|
|
}
|
|
|
|
public function fax_succeeds() {
|
|
$nl = Notification_Log::q("select * from notification_log where id_order=? and type='phaxio' and status='success'", [$this->id_order]);
|
|
return $nl->count() ? true : false;
|
|
}
|
|
|
|
public function transaction() {
|
|
return $this->_txn;
|
|
}
|
|
|
|
public function actions(){
|
|
return Crunchbutton_Order_Action::byOrder( $this->id_order );
|
|
}
|
|
|
|
public function date_delivery( $timezone = 'restaurant' ){
|
|
if( $this->date_delivery ){
|
|
if (!isset($this->_date_delivery)) {
|
|
$this->_date_delivery = new DateTime($this->date_delivery, new DateTimeZone(c::config()->timezone));
|
|
if( $timezone == 'restaurant' ){
|
|
$this->_date_delivery->setTimezone(new DateTimeZone($this->restaurant()->timezone));
|
|
}
|
|
}
|
|
return $this->_date_delivery;
|
|
}
|
|
}
|
|
|
|
public function date( $reset = false ) {
|
|
if( $reset ){
|
|
$this->_date = null;
|
|
}
|
|
if (!isset($this->_date) || !$this->_date) {
|
|
if( $this->preordered && $this->preordered_date ){
|
|
$this->_date = new DateTime($this->preordered_date, new DateTimeZone(c::config()->timezone));
|
|
$this->_date->setTimezone(new DateTimeZone($this->restaurant()->timezone));
|
|
} else {
|
|
$this->_date = new DateTime($this->date, new DateTimeZone(c::config()->timezone));
|
|
$this->_date->setTimezone(new DateTimeZone($this->restaurant()->timezone));
|
|
}
|
|
}
|
|
return $this->_date;
|
|
}
|
|
|
|
public function dateAtTz( $timezone ) {
|
|
$date = new DateTime( $this->date, new DateTimeZone( c::config()->timezone ) );
|
|
$date->setTimezone( new DateTimeZone( $timezone ) );
|
|
return $date;
|
|
}
|
|
|
|
public function verifyPayment() {
|
|
switch ($this->pay_type) {
|
|
case 'cash':
|
|
$status = true;
|
|
break;
|
|
|
|
case 'card':
|
|
|
|
// campus cash
|
|
if( $this->campus_cash ){
|
|
|
|
$campus_money = new Crunchbutton_Stripe_Campus_Cash;
|
|
$success = $campus_money->store( [
|
|
'campus_cash' => $this->campusCash,
|
|
'name' => $this->name,
|
|
'email' => $this->email ] );
|
|
|
|
if( $success && $success[ 'campus_cash_sha1' ] ){
|
|
$this->_campus_cash_sha1 = $success[ 'campus_cash_sha1' ];
|
|
$this->_customer = $success['customer'];
|
|
$status = true;
|
|
}
|
|
|
|
} else {
|
|
|
|
$user = c::user()->id_user ? c::user() : null;
|
|
|
|
if ($user) {
|
|
$paymentType = $user->payment_type();
|
|
}
|
|
|
|
// use a stored users card and the apporiate payment type
|
|
|
|
if (!$this->_card['id'] && $paymentType->id_user_payment_type) {
|
|
|
|
if (Crunchbutton_User_Payment_Type::processor() == 'stripe' && $paymentType->stripe_id) {
|
|
$charge = new Charge_Stripe([
|
|
'card_id' => $paymentType->stripe_id,
|
|
'customer_id' => $user->stripe_id
|
|
]);
|
|
|
|
} else {
|
|
// there is a mismatch with stripe and balanced
|
|
}
|
|
}
|
|
|
|
// create the objects with no params
|
|
if (!$charge) {
|
|
switch (Crunchbutton_User_Payment_Type::processor()) {
|
|
case 'stripe':
|
|
$charge = new Charge_Stripe([
|
|
'customer_id' => $user->stripe_id
|
|
]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the amount is 0 it means that the user used his credit.
|
|
$amount = $this->calcFinalPriceMinusUsersCredit();
|
|
Log::debug([ 'issue' => '#1551', 'method' => 'verifyPayment', '$this->final_price' => $this->final_price, 'giftcardValue'=> $this->giftcardValue, 'amount' => $amount ]);
|
|
|
|
|
|
// issue #3145
|
|
if ($amount > .5) {
|
|
$r = $charge->charge([
|
|
'amount' => $amount,
|
|
'card' => $this->_card,
|
|
'name' => $this->name,
|
|
'address' => $this->address,
|
|
'email' => $user->email,
|
|
'phone' => $this->phone,
|
|
'user' => $user,
|
|
'restaurant' => $this->restaurant()
|
|
]);
|
|
if ($r['status']) {
|
|
$this->_txn = $r['txn'];
|
|
$this->_user = $user;
|
|
$this->_customer = $r['customer'];
|
|
$this->_paymentType = $r['card'];
|
|
$status = true;
|
|
} else {
|
|
$status = $r;
|
|
}
|
|
|
|
} elseif ($amount > 0) {
|
|
// we just gave them 50c or something
|
|
Log::debug([
|
|
'issue' => '#3145',
|
|
'method' => 'verifyPayment',
|
|
'$this->final_price' => $this->final_price,
|
|
'giftcardValue'=> $this->giftcardValue,
|
|
'amount' => $amount
|
|
]);
|
|
$status = true;
|
|
|
|
} else {
|
|
$status = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
public static function recent() {
|
|
return self::q('select * from `order` order by `date` DESC');
|
|
}
|
|
|
|
public static function deliveryOrders( $hours = 24, $all = false, $admin = null){
|
|
if (c::admin()->getConfig('demo')->value == '1') {
|
|
//$restaurant = Restaurant::q('select * from restaurant where name="devins driver test restaurant"');
|
|
$query = '
|
|
select o.* from `order` o
|
|
left join restaurant r using(id_restaurant)
|
|
where r.name like "%test restaurant%"
|
|
and r.delivery_service=1
|
|
limit 10
|
|
';
|
|
|
|
} else {
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- ' . $hours . ' hours' );
|
|
$interval = $now->format( 'Y-m-d H:i:s' );
|
|
|
|
if( !$all ){
|
|
if (!$admin) {
|
|
$admin = c::admin();
|
|
}
|
|
$deliveryFor = $admin->allPlacesHeDeliveryFor();
|
|
if( count( $deliveryFor ) == 0 ){
|
|
$deliveryFor[] = 0;
|
|
}
|
|
$where = 'WHERE o.id_restaurant IN( ' . join( ',', $deliveryFor ) . ' )';
|
|
} else {
|
|
$where = 'WHERE 1=1 ';
|
|
}
|
|
|
|
$where .= ' AND o.delivery_service = true AND date > ? ';
|
|
$query = 'SELECT DISTINCT( o.id_order ) id, o.* FROM `order` o ' . $where . ' ORDER BY o.id_order';
|
|
}
|
|
return Order::q($query, [$interval]);
|
|
}
|
|
|
|
public static function deliveryOrdersByCommunity($hours, $id_community){
|
|
// Pre-orders that haven't been processed and don't have a date are not included
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- ' . $hours . ' hours' );
|
|
$interval = $now->format( 'Y-m-d H:i:s' );
|
|
|
|
$query = 'SELECT DISTINCT( o.id_order ) id, o.* FROM `order` o WHERE o.delivery_service=true and date > ? and date is not null and id_community = ? ORDER BY o.id_order';
|
|
return Order::q($query, [$interval, $id_community]);
|
|
}
|
|
|
|
public static function deliveryOrdersByCommunityBeforeNow($hours, $id_community){
|
|
// For backtest/simulation purposes
|
|
// Note that pre-orders are not removed, but it's not clear how to screen them out.
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$nowString = $now->format( 'Y-m-d H:i:s' );
|
|
$now->modify( '- ' . $hours . ' hours' );
|
|
$interval = $now->format( 'Y-m-d H:i:s' );
|
|
|
|
$query = 'SELECT DISTINCT( o.id_order ) id, o.* FROM `order` o WHERE o.delivery_service=true and date > ? and date <= ? and date is not null and id_community = ? ORDER BY o.id_order';
|
|
return Order::q($query, [$interval, $nowString, $id_community]);
|
|
}
|
|
|
|
/*
|
|
Logic to make sure that admin doesn't see orders :
|
|
|
|
1. Order action has been taken.
|
|
2. Order is not in the priority list and the community has the logistics system activated and it's been more
|
|
than a minute
|
|
3. Order is in the priority list and priority has expired.
|
|
IMPORTANT: This logic here does not screen by admin, and so the priority expiration must be the same for all
|
|
drivers. Otherwise this code will break.
|
|
4. Order is in the priority list and priority had not expired and admin is in the priority list and admin does not have low priority.
|
|
|
|
IMPORTANT:
|
|
Note that if a new low priority type is added, this query may need to be rewritten.
|
|
*/
|
|
public static function deliveryOrdersForAdminOnly( $hours = 24, $admin = null){
|
|
|
|
if (c::admin()->getConfig('demo')->value == '1') {
|
|
//$restaurant = Restaurant::q('select * from restaurant where name="devins driver test restaurant"');
|
|
//TODO: This code was left untouched from the original deliveryOrders code. May not work as expected
|
|
// in the demo environment.
|
|
$query = '
|
|
select o.* from `order` o
|
|
left join restaurant r using(id_restaurant)
|
|
where r.name like "%test restaurant%"
|
|
and r.delivery_service=1
|
|
limit 10
|
|
';
|
|
return Order::q($query);
|
|
|
|
} else {
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$nowString = $now->format( 'Y-m-d H:i:s' );
|
|
$now->modify( '- ' . $hours . ' hours' );
|
|
$interval = $now->format( 'Y-m-d H:i:s' );
|
|
$now->modify( '+ ' . $hours . ' hours' );
|
|
// TODO: Hardwired constant here - not ideal
|
|
$now->modify( '- 3 minutes' );
|
|
$interval3Min = $now->format( 'Y-m-d H:i:s' );
|
|
$now->modify( '+ 3 minutes' );
|
|
|
|
$now->modify( '+ 6 hours' );
|
|
$preorder_date = $now->format( 'Y-m-d H:i:s' );
|
|
|
|
|
|
if (!$admin) {
|
|
$admin = c::admin();
|
|
}
|
|
$deliveryFor = $admin->allPlacesHeDeliveryFor();
|
|
if( count( $deliveryFor ) == 0 ){
|
|
$deliveryFor[] = 0;
|
|
}
|
|
$where = 'o.id_restaurant IN( ' . join( ',', $deliveryFor ) . ' )';
|
|
|
|
$query = 'SELECT DISTINCT(o.id_order) as id, o.* FROM `order` as o ' .
|
|
'inner join community as c using (id_community) ' .
|
|
'left outer join order_action as oa on o.delivery_status = oa.id_order_action ' .
|
|
'left outer join order_priority as op on op.id_order = o.id_order
|
|
where (oa.id_order is not null or ' .
|
|
'(op.id_order is null and ((c.delivery_logistics is null) or (o.date < ? and ' .
|
|
'c.delivery_logistics is not null) or (o.preordered=1))) or (op.id_order is not null and op.priority_expiration < ?) ' .
|
|
'or (op.id_order is not null and op.priority_expiration >= ? and op.id_admin = ? '.
|
|
'and op.priority_given != ?)) and o.delivery_service=true and o.delivery_type = "delivery" and ( o.date > ? OR ( o.preordered = 1 and o.date_delivery < ? AND o.date_delivery > ? ) )'.
|
|
'and ' . $where . ' ORDER BY o.id_order';
|
|
// $op = Crunchbutton_Order_Priority::PRIORITY_LOW;
|
|
// print "The query params: $nowString, $nowString, $admin->id_admin, $op, $interval\n";
|
|
|
|
return Order::q($query, [$interval3Min, $nowString, $nowString, $admin->id_admin,
|
|
Crunchbutton_Order_Priority::PRIORITY_LOW, $interval, $preorder_date, $interval]);
|
|
|
|
//
|
|
}
|
|
|
|
}
|
|
|
|
public static function outstandingOrders(){
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now = $now->format( 'Y-m-d' );
|
|
|
|
$query = "SELECT id_order,
|
|
TIMESTAMPDIFF(HOUR, o.date, NOW()) AS hours
|
|
FROM `order` o
|
|
WHERE o.delivery_type = 'delivery'
|
|
AND o.delivery_service = true
|
|
AND o.id_order NOT IN
|
|
(SELECT id_order
|
|
FROM order_action
|
|
WHERE type = 'delivery-delivered')
|
|
-- remove the commment below to get this the orders from today
|
|
AND o.date BETWEEN '{$now} 00:00:00' AND '{$now} 23:59:59'
|
|
HAVING hours >= 2 ORDER BY id_order DESC ";
|
|
return Order::q( $query );
|
|
|
|
}
|
|
|
|
public static function deliveryOrderTimes( $hours = 24, $all = false ){
|
|
|
|
$id_admin = c::admin()->id_admin;
|
|
if( !$all ){
|
|
$admin = Admin::o( $id_admin );
|
|
$deliveryFor = $admin->allPlacesHeDeliveryFor();
|
|
if( count( $deliveryFor ) == 0 ){
|
|
$deliveryFor[] = 0;
|
|
}
|
|
$where = 'WHERE o.id_restaurant IN( ' . join( ',', $deliveryFor ) . ' )';
|
|
} else {
|
|
$where = 'WHERE 1=1 ';
|
|
}
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
|
|
$now->modify( ' - ' . $hours . ' hour' );
|
|
$interval = $now->format( 'Y-m-d H:i:s' );
|
|
$where .= ' AND o.delivery_service = true ';
|
|
$where .= ' AND date > ? ';
|
|
|
|
$query = 'SELECT DISTINCT( o.id_order ) id, o.* FROM `order` o ' . $where . ' ORDER BY o.id_order';
|
|
|
|
return Order::q( $query,[$interval]);
|
|
}
|
|
|
|
public function revenueByAdminPeriod( $id_admin, $date_start, $date_end ){
|
|
//convert to La timezone
|
|
$date_start = new DateTime( $date_start, new DateTimeZone( c::config()->timezone ) );
|
|
$date_end = new DateTime( $date_end, new DateTimeZone( c::config()->timezone ) );
|
|
|
|
//get orders at this period
|
|
$query = 'SELECT DISTINCT( o.id_order ) id, oa.* FROM `order` o
|
|
INNER JOIN order_action oa ON oa.id_order = o.id_order
|
|
WHERE
|
|
oa.id_admin = ?
|
|
AND o.date >= ?
|
|
AND o.date <= ?';
|
|
return Crunchbutton_Order_Action::q( $query, [$id_admin, $date_start->format( 'Y-m-d H:i:s' ), $date_end->format( 'Y-m-d H:i:s' )]);
|
|
}
|
|
|
|
public static function deliveredByCBDrivers( $search ){
|
|
|
|
$where = ' WHERE 1 = 1';
|
|
$innerJoin = ' ';
|
|
|
|
if( $search[ 'id_admin' ] ){
|
|
$innerJoin .= ' INNER JOIN order_action oa ON oa.id_order = o.id_order AND oa.id_admin = ' . $search[ 'id_admin' ];
|
|
}
|
|
|
|
if( $search[ 'id_restaurant' ] ){
|
|
$where .= ' AND o.id_restaurant = ' . $search[ 'id_restaurant' ];
|
|
}
|
|
|
|
if ( $search[ 'start' ] ) {
|
|
$s = new DateTime( $search[ 'start' ] );
|
|
$where .= ' AND DATE( `o.date`) >= \'' . $s->format( 'Y-m-d' ) . '\'';
|
|
}
|
|
|
|
if ( $search[ 'end' ] ) {
|
|
$s = new DateTime( $search[ 'end' ] );
|
|
$where .= ' AND DATE( `o.date` ) <= \'' . $s->format( 'Y-m-d' ) . '\'';
|
|
}
|
|
|
|
if( $search[ 'limit' ] ){
|
|
$limit = 'LIMIT '. $search[ 'limit' ];
|
|
} else {
|
|
$limit = 'LIMIT 25';
|
|
}
|
|
|
|
$query = 'SELECT DISTINCT(o.id_order) id, o.* FROM `order` o
|
|
INNER JOIN restaurant r ON r.id_restaurant = o.id_restaurant AND r.delivery_service = true
|
|
' . $innerJoin . $where . '
|
|
ORDER BY o.id_order DESC ' . $limit;
|
|
|
|
return Order::q( $query );
|
|
|
|
}
|
|
|
|
public static function find($search = []) {
|
|
$query = 'SELECT credit.total AS credit, promo.total AS gift_card, support.id_support, a.browser, a.os, o.* from `order` o left join restaurant using(id_restaurant)
|
|
LEFT JOIN( SELECT id_order_reference, SUM( value ) as total FROM credit WHERE ( credit_type = "cash" OR credit_type != "point" ) AND id_order_reference IS NOT NULL AND id_promo IS NULL GROUP BY id_order_reference ) credit ON credit.id_order_reference = o.id_order
|
|
LEFT JOIN( SELECT id_order_reference, SUM( value ) as total FROM promo WHERE id_order_reference IS NOT NULL GROUP BY id_order_reference ) promo ON promo.id_order_reference = o.id_order
|
|
LEFT JOIN ( SELECT MAX( id_support ) AS id_support, id_order FROM support WHERE id_order IS NOT NULL GROUP BY id_order ) support ON support.id_order = o.id_order
|
|
LEFT JOIN agent a ON a.id_agent = o.id_agent
|
|
WHERE o.id_order IS NOT NULL';
|
|
|
|
if ($search['env']) {
|
|
$query .= ' and o.env="'.$search['env'].'" ';
|
|
}
|
|
if ($search['processor']) {
|
|
$query .= ' and o.processor="'.$search['processor'].'" ';
|
|
}
|
|
if ($search['start']) {
|
|
$s = new DateTime($search['start']);
|
|
$query .= ' and DATE(o.`date`)>="'.$s->format('Y-m-d').'" ';
|
|
}
|
|
if ($search['end']) {
|
|
$s = new DateTime($search['end']);
|
|
$query .= ' and DATE(o.`date`)<="'.$s->format('Y-m-d').'" ';
|
|
}
|
|
|
|
$hasPermissionToAllRestaurants = c::admin()->permission()->check( [ 'global', 'orders-all' ] );
|
|
|
|
if ($search['restaurant']) {
|
|
if( $hasPermissionToAllRestaurants || c::admin()->permission()->check( [ "orders-list-restaurant-{$search['restaurant']}" ] ) ){
|
|
$query .= ' and `o`.id_restaurant="'.$search['restaurant'].'" ';
|
|
} else {
|
|
exit;
|
|
}
|
|
} else {
|
|
// If the user doesnt have permission to all restaurants show just the ones he could see
|
|
if( !$hasPermissionToAllRestaurants ){
|
|
$restaurants = c::admin()->getRestaurantsUserHasPermissionToSeeTheirOrders();
|
|
$restaurants[] = 0;
|
|
$query .= ' and o.id_restaurant IN ( ' . join( $restaurants, ',' ) . ')';
|
|
}
|
|
}
|
|
|
|
if ($search['community']) {
|
|
$query .= ' and `restaurant`.community="'.$search['community'].'" ';
|
|
}
|
|
if ($search['order']) {
|
|
$query .= ' and o.id_order="'.$search['order'].'" ';
|
|
}
|
|
|
|
if ($search['search']) {
|
|
$qn = '';
|
|
$q = '';
|
|
$searches = explode(' ',$search['search']);
|
|
foreach ($searches as $word) {
|
|
if ($word{0} == '-') {
|
|
$qn .= ' and o.name not like "%'.substr($word,1).'%" ';
|
|
$qn .= ' and o.address not like "%'.substr($word,1).'%" ';
|
|
$qn .= ' and o.phone not like "%'.substr($word,1).'%" ';
|
|
$qn .= ' and `restaurant`.name not like "%'.substr($word,1).'%" ';
|
|
} else {
|
|
$q .= '
|
|
and (o.name like "%'.$word.'%"
|
|
or o.id_order = "'.$word.'"
|
|
or o.address like "%'.$word.'%"
|
|
or `restaurant`.name like "%'.$word.'%"
|
|
or REPLACE( o.phone, "-", "" ) like "%'. ( str_replace( '-' , '', $word ) ) .'%")
|
|
';
|
|
}
|
|
}
|
|
$query .= $q.$qn;
|
|
}
|
|
|
|
$query .= '
|
|
order by `date` DESC
|
|
';
|
|
|
|
if ($search['limit']) {
|
|
$query .= ' limit '.$search['limit'].' ';
|
|
}
|
|
|
|
$orders = self::q($query);
|
|
return $orders;
|
|
}
|
|
|
|
public function dishes( $forceLoad = false ) {
|
|
if( $forceLoad ){
|
|
$this->_dishes = false;
|
|
unset( $this->_dishes );
|
|
}
|
|
if ( !$this->_dishes ) {
|
|
$this->_dishes = Order_Dish::q( 'SELECT * FROM order_dish WHERE id_order = ?', [$this->id_order]);
|
|
}
|
|
return $this->_dishes;
|
|
}
|
|
|
|
public function tip() {
|
|
if( $this->tip_type == self::TIP_NUMBER ){
|
|
return number_format( $this->tip, 2 );
|
|
} else {
|
|
/* - to calculate the tip it must use as reference the price with mark up
|
|
because the marked up price was the one shown the the user
|
|
- see talk between pererinha and david at hipchat 02/17/2014
|
|
https://github.com/crunchbutton/crunchbutton/issues/2248#issuecomment-35381055 */
|
|
if( $this->price_plus_delivery_markup && $this->price_plus_delivery_markup > 0 ){
|
|
$tip = ( $this->price_plus_delivery_markup * ( $this->tip / 100 ) );
|
|
} else {
|
|
$tip = ( $this->price * ( $this->tip / 100 ) );
|
|
}
|
|
return number_format( $tip, 2 );
|
|
}
|
|
}
|
|
|
|
public function tax() {
|
|
/* - taxes should be calculated using the price without markup
|
|
- if restaurant uses 3rd party delivery service remove the delivery_fee
|
|
- see #2236 and #2248
|
|
-> Removed the Util::ceil - see #2613
|
|
*/
|
|
if($this->delivery_service){
|
|
$baseToCalcTax = $this->price;
|
|
} else {
|
|
$baseToCalcTax = $this->price + $this->delivery_fee;
|
|
}
|
|
return $tax = number_format( round( $baseToCalcTax * ( $this->tax / 100 ), 2 ), 2 );;
|
|
}
|
|
|
|
public function deliveryFee() {
|
|
return number_format($this->delivery_fee,2);
|
|
}
|
|
|
|
public function serviceFee() {
|
|
$fee = number_format(($this->price + $this->delivery_fee) * ($this->service_fee/100),2);
|
|
if( $this->campus_cash && $this->campusCashFee() ){
|
|
$fee += number_format(($this->price + $this->delivery_fee) * ($this->campusCashFee()/100),2);
|
|
}
|
|
return $fee;
|
|
}
|
|
|
|
public function cbFee() {
|
|
return ($this->restaurant_fee_percent()) * ($this->price) / 100;
|
|
}
|
|
|
|
public function customer_fee(){
|
|
return ($this->restaurant()->fee_customer) * ($this->price) / 100;
|
|
}
|
|
|
|
public function restaurant_fee_percent(){
|
|
return ( !is_null( $this->fee_restaurant ) ) ? $this->fee_restaurant : $this->restaurant()->fee_restaurant;
|
|
}
|
|
|
|
public function fee(){
|
|
if( $this->restaurant()->fee_on_subtotal ){
|
|
return $this->cbFee();
|
|
} else {
|
|
return $this->restaurant_fee_percent() * ($this->final_price) / 100;
|
|
}
|
|
}
|
|
|
|
public function notify(){
|
|
$this->notifyRestaurants();
|
|
if($this->restaurant()->delivery_service){
|
|
$this->notifyDrivers();
|
|
}
|
|
}
|
|
|
|
// #2236
|
|
public function restaurantPrice(){
|
|
|
|
$delivery_service_markup = ( $this->delivery_service_markup ) ? $this->delivery_service_markup : 0;
|
|
|
|
if( intval( $delivery_service_markup ) > 0 && !$this->final_price_plus_delivery_markup ){
|
|
return number_format( $this->final_price - $this->delivery_service_markup_value , 2);
|
|
} else {
|
|
if( intval( $delivery_service_markup ) > 0 ){
|
|
return number_format( $this->final_price - $this->cb_service_fee, 2 );
|
|
} else {
|
|
return number_format( $this->final_price_plus_delivery_markup - $this->cb_service_fee, 2 );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public function notifyRestaurants() {
|
|
foreach ( $this->restaurant()->notifications() as $n ) {
|
|
// admin type is depreciated. so lets not use it
|
|
if ($n->type == Crunchbutton_Notification::TYPE_ADMIN) {
|
|
continue;
|
|
}
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'sending notification', 'type' => $n->type, 'to' => $n->value, 'type' => 'notification']);
|
|
$n->send( $this );
|
|
}
|
|
}
|
|
|
|
// return a list of drivers that are currently working for the community to notify
|
|
public function getDriversToNotify() {
|
|
$drivers = Crunchbutton_Community_Shift::driversCouldDeliveryOrder($this->id_order);
|
|
return $drivers;
|
|
}
|
|
|
|
|
|
public function notifyDrivers(){
|
|
|
|
if( $this->ignoreDrivers ){
|
|
return;
|
|
}
|
|
|
|
$order = $this;
|
|
$needDrivers = false;
|
|
$hasDriversWorking = false;
|
|
|
|
// check if the restaurant is using our delivery system
|
|
if($order->restaurant()->delivery_service){
|
|
$needDrivers = true;
|
|
}
|
|
|
|
// When the pre-order is processed because a driver has accepted we dont send driver's notification #7978
|
|
if($this->preordered){
|
|
$status = $this->status()->last();
|
|
if( $status[ 'status' ] != 'new' ){
|
|
return;
|
|
}
|
|
}
|
|
|
|
$drivers = $this->getDriversToNotify();
|
|
if( $drivers ){
|
|
foreach( $drivers as $driver ){
|
|
foreach( $driver->activeNotifications() as $adminNotification ){
|
|
$adminNotification->send( $order );
|
|
$hasDriversWorking = true;
|
|
$message = '#'.$order->id_order.' sending driver notification to ' . $driver->name . ' #' . $adminNotification->value;
|
|
Log::debug( [ 'order' => $order->id_order, 'action' => $message, 'type' => 'delivery-driver' ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
Crunchbutton_Admin_Notification_Log::register( $this->id_order, ' Order::notifyDrivers' );
|
|
|
|
if( $needDrivers && !$hasDriversWorking ){
|
|
Crunchbutton_Admin_Notification::warningAboutNoRepsWorking( $order );
|
|
}
|
|
}
|
|
|
|
public function checkBeforeNotifications($drivers){
|
|
|
|
if( $this->ignoreDrivers ){
|
|
return null;
|
|
}
|
|
|
|
$retVal = ['needDrivers' => false, 'hasDriversWorking' => false];
|
|
|
|
// check if the restaurant is using our delivery system
|
|
if($this->restaurant()->delivery_service){
|
|
$retVal['needDrivers'] = true;
|
|
}
|
|
|
|
if( $drivers ){
|
|
foreach( $drivers as $driver ){
|
|
if ($driver->activeNotifications()->count() > 0){
|
|
$retVal['hasDriversWorking'] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return $retVal;
|
|
|
|
}
|
|
|
|
public function registerAfterNotifications($id_admin, $seconds){
|
|
|
|
Crunchbutton_Admin_Notification_Log::registerWithAdminForLogistics($this->id_order, $id_admin, $seconds, 0);
|
|
|
|
}
|
|
|
|
|
|
public function checkForNoRepsNotifications($needDrivers, $hasDriversWorking){
|
|
|
|
if( $needDrivers && !$hasDriversWorking ){
|
|
Crunchbutton_Admin_Notification::warningAboutNoRepsWorking($this);
|
|
}
|
|
}
|
|
|
|
public function driver(){
|
|
if( !$this->_driver && $this->id_order ){
|
|
$this->_driver = Admin::q('SELECT a.* FROM order_action oa INNER JOIN admin a ON a.id_admin = oa.id_admin WHERE oa.id_order = ? AND type != "delivery-rejected" ORDER BY id_order_action DESC LIMIT 1', [$this->id_order]);
|
|
}
|
|
return $this->_driver;
|
|
}
|
|
|
|
public function resend_notify_drivers(){
|
|
$order = $this;
|
|
Crunchbutton_Admin_Notification_Log::cleanLog( $order->id_order );
|
|
$order->notifyDrivers();
|
|
return true;
|
|
}
|
|
|
|
public function resend_notify(){
|
|
$order = $this;
|
|
// Log::debug([ 'order' => $order->id_order, 'action' => 'restarting starting notification', 'type' => 'notification']);
|
|
// Delete all the notification log in order to start a new one
|
|
// Notification_Log::DeleteFromOrder( $order->id_order );
|
|
// Log::debug([ 'order' => $order->id_order, 'action' => 'deleted previous notifications', 'type' => 'notification']);
|
|
Crunchbutton_Admin_Notification_Log::cleanLog( $order->id_order );
|
|
$order->ignoreDrivers = true;
|
|
$order->notify();
|
|
return true;
|
|
}
|
|
|
|
public function confirm() {
|
|
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'confirm() - dial confirm call', '$this->confirmed' => $this->confirmed, '$this->restaurant()->confirmation' =>$this->restaurant()->confirmation, 'type' => 'notification']);
|
|
|
|
if ($this->confirmed || !$this->restaurant()->confirmation) {
|
|
return;
|
|
}
|
|
|
|
// the restaurant asked crunchbutton to call it, stop sending confirmations call - See #2848
|
|
if( $this->asked_to_call ){
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'asked_to_call() - dial confirm call', '$this->asked_to_call' => $this->asked_to_call, '$this->restaurant()->confirmation' =>$this->restaurant()->confirmation, 'type' => 'notification']);
|
|
return;
|
|
}
|
|
|
|
$nl = Notification_Log::q("SELECT * FROM notification_log WHERE id_order=? AND type = 'confirm' AND ( status = 'created' OR status = 'queued' OR status ='success' )", [$this->id_order]);
|
|
if( $nl->count() > 0 ){
|
|
// Log
|
|
Log::debug([ 'order' => $this->id_order, 'count' => $nl->count(), 'action' => 'confirmation call already in process', 'host' => c::config()->host_callback, 'type' => 'notification']);
|
|
return;
|
|
}
|
|
|
|
$env = c::getEnv();
|
|
|
|
$num = ($env == 'live' ? $this->restaurant()->phone : c::config()->twilio->testnumber);
|
|
|
|
// Added new confirmation type: stealth. More 'Stealth confirmation call' #2848
|
|
if( $this->restaurant()->confirmation_type == 'stealth' ){
|
|
$confirmURL = 'https://'.c::config()->host_callback.'/api/order/'.$this->id_order.'/doconfirmstealth';
|
|
} else {
|
|
$confirmURL = 'https://'.c::config()->host_callback.'/api/order/'.$this->id_order.'/doconfirm';
|
|
}
|
|
|
|
// Log
|
|
Log::debug([ 'order' => $this->id_order, 'num' => $num, 'confirmURL' => $confirmURL, 'action' => 'dial confirm call', 'count' => $nl->count(), 'num' => $num, 'host' => c::config()->host_callback, 'callback' => $callback, 'type' => 'notification']);
|
|
$log = new Notification_Log;
|
|
$log->type = 'confirm';
|
|
$log->id_order = $this->id_order;
|
|
$log->date = date('Y-m-d H:i:s');
|
|
$log->status = 'created';
|
|
$log->save();
|
|
|
|
|
|
$twilio = c::twilio();
|
|
|
|
$call = $twilio->account->calls->create(
|
|
c::config()->twilio->{$env}->outgoingRestaurant,
|
|
'+1'.$num,
|
|
$confirmURL,
|
|
[
|
|
'StatusCallback' => 'https://'.c::config()->host_callback.'/api/notification/'.$log->id_notification_log.'/confirm'
|
|
]
|
|
);
|
|
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'dial confirm call sent', 'confirmURL' => $confirmURL, 'count' => $nl->count(), 'num' => $num, 'host' => c::config()->host_callback, 'callback' => $callback, 'type' => 'notification']);
|
|
|
|
$log->remote = $call->sid;
|
|
$log->status = $call->status;
|
|
$log->save();
|
|
}
|
|
|
|
public function receipt() {
|
|
$message = Crunchbutton_Message_Sms::greeting( $this->user()->firstName() );
|
|
$message .= $this->message('selfsms');
|
|
Crunchbutton_Message_Sms::send([
|
|
'to' => $this->phone,
|
|
'message' => $message,
|
|
'reason' => Crunchbutton_Message_Sms::REASON_CUSTOMER_ORDER
|
|
]);
|
|
$this->sendNativeAppLink();
|
|
}
|
|
|
|
public function tellCustomerTheOrderWasCanceled(){
|
|
$message = Crunchbutton_Message_Sms::greeting( $this->user()->firstName() );
|
|
if ($this->pay_type == self::PAY_TYPE_CREDIT_CARD) {
|
|
$message .= "Your order #".$this->id_order." was cancelled and refunded to your card.\n";
|
|
} else {
|
|
$message .= "Your order #".$this->id_order." was cancelled.\n";
|
|
}
|
|
$message .= "\nCrunchbutton.com";
|
|
|
|
Crunchbutton_Message_Sms::send([
|
|
'to' => $this->phone,
|
|
'message' => $message,
|
|
'reason' => Crunchbutton_Message_Sms::REASON_CUSTOMER_ORDER
|
|
]);
|
|
}
|
|
|
|
public function que() {
|
|
$q = Queue::create([
|
|
'type' => Crunchbutton_Queue::TYPE_ORDER,
|
|
'id_order' => $this->id_order,
|
|
'seconds' => 3
|
|
]);
|
|
}
|
|
|
|
public function receiptSignature(){
|
|
$user = $this->user();
|
|
if( $user->email ){
|
|
$mail = new Email_Order([
|
|
'order' => $this,
|
|
'signature' => true,
|
|
'email' => $user->email,
|
|
'user' => true,
|
|
]);
|
|
$mail->send();
|
|
}
|
|
}
|
|
|
|
public function queConfirm() {
|
|
|
|
$order = $this;
|
|
|
|
if ($order->confirmed || !$order->restaurant()->confirmation) {
|
|
return;
|
|
}
|
|
// Check if there are another confirm que, if it does it will not send two confirms. Just one is enough.
|
|
$nl = Notification_Log::q("SELECT * FROM notification_log WHERE id_order=? AND type = 'confirm' AND ( status = 'created' OR status = 'queued' )", [$order->id_order]);
|
|
|
|
if( $nl->count() > 0 ){
|
|
return;
|
|
}
|
|
|
|
// Query to count the number of confirmations sent
|
|
$nl = Notification_Log::q("SELECT * FROM notification_log WHERE id_order=? AND status='callback' AND `type`='confirm'", [$order->id_order]);
|
|
|
|
if( $nl->count() > 0 ){ // if it is the 2nd, 3rd, 4th... call the confirmation time should be 2 min even to hasFaxNotification - #974
|
|
$confirmationTime = c::config()->twilio->confirmTimeCallback;
|
|
|
|
} else { // if it is the first confirmation call
|
|
|
|
if( $order->restaurant()->hasFaxNotification() ){ // If restaurant has fax notification
|
|
$confirmationTime = c::config()->twilio->confirmFaxTime;
|
|
} else {
|
|
$confirmationTime = c::config()->twilio->confirmTime;
|
|
}
|
|
}
|
|
|
|
// Log
|
|
Log::debug( [ 'order' => $this->id_order, 'action' => 'queConfirm - confirm', 'hasFaxNotification' => $order->restaurant()->hasFaxNotification(), 'confirmationTime' => $confirmationTime, 'confirmation number' => $nl->count(), 'confirmed' => $this->confirmed, 'type' => 'notification' ] );
|
|
|
|
$q = Queue::create([
|
|
'type' => Crunchbutton_Queue::TYPE_ORDER_CONFIRM,
|
|
'id_order' => $this->id_order,
|
|
'seconds' => $confirmationTime / 1000
|
|
]);
|
|
|
|
}
|
|
|
|
// At the method warningOrderNotConfirmed() i've tried to use $this->confirmed
|
|
// but it always returns an empty string, so I had to create this method.
|
|
public function isConfirmed( $id_order ){
|
|
$order = Order::o( $id_order );
|
|
if( $order->id_order ){
|
|
if( $order->confirmed ){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function warningStealthNotConfirmed(){
|
|
|
|
$order = $this;
|
|
|
|
$isConfirmed = Order::isConfirmed( $this->id_order );
|
|
|
|
Log::debug( [ 'order' => $this->id_order, 'action' => 'warningStealthNotConfirmed', 'object' => $order->json(), 'type' => 'notification' ]);
|
|
|
|
if ( $isConfirmed ) {
|
|
Log::debug( [ 'order' => $this->id_order, 'action' => 'que warningStealthNotConfirmed ignored', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
return;
|
|
}
|
|
|
|
$date = $order->date();
|
|
$date = $date->format( 'M jS Y' ) . ' - ' . $date->format( 'g:i:s A' );
|
|
|
|
$env = c::getEnv();
|
|
|
|
$message = "Please call {$order->restaurant()->name} in {$order->restaurant()->community()->name} ({$order->restaurant()->phone()}). They pressed 2 to say they didn't receive the fax for Order #{$order->id_order}";
|
|
|
|
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningStealthNotConfirmed sending sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
|
|
// keep this ugly true for tests only
|
|
if( $env == 'live' ){
|
|
|
|
Crunchbutton_Message_Sms::send([
|
|
'to' => Crunchbutton_Support::getUsers(),
|
|
'message' => $message,
|
|
'reason' => Crunchbutton_Message_Sms::REASON_SUPPORT_WARNING
|
|
]);
|
|
|
|
} else {
|
|
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningStealthNotConfirmed DEV dont send sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
}
|
|
|
|
}
|
|
public function warningOrderNotConfirmed(){
|
|
return;
|
|
$order = $this;
|
|
|
|
$isConfirmed = Order::isConfirmed( $this->id_order );
|
|
|
|
Log::debug( [ 'order' => $this->id_order, 'action' => 'warningOrderNotConfirmed', 'object' => $order->json(), 'type' => 'notification' ]);
|
|
|
|
if ( $isConfirmed || !$this->restaurant()->confirmation ) {
|
|
Log::debug( [ 'order' => $this->id_order, 'action' => 'que warningOrderNotConfirmed ignored', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
return;
|
|
}
|
|
|
|
$date = $order->date();
|
|
$date = $date->format( 'M jS Y' ) . ' - ' . $date->format( 'g:i:s A' );
|
|
|
|
$env = c::getEnv();
|
|
|
|
$message = 'O# ' . $order->id_order . ' for ' . $order->restaurant()->name . ' (' . $date . ') not confirmed.';
|
|
$message .= "\n";
|
|
$message .= 'R# ' . $order->restaurant()->phone();
|
|
$message .= "\n";
|
|
$message .= 'C# ' . $order->user()->name . ' : ' . $order->phone();
|
|
$message .= "\n";
|
|
$message .= 'E# ' . $env;
|
|
|
|
|
|
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningOrderNotConfirmed sending sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
|
|
$twilio = c::twilio();
|
|
|
|
if( $env == 'live' ){
|
|
|
|
Crunchbutton_Message_Sms::send([
|
|
'to' => Crunchbutton_Support::getUsers(),
|
|
'message' => $message,
|
|
'reason' => Crunchbutton_Message_Sms::REASON_SUPPORT_WARNING
|
|
]);
|
|
|
|
} else {
|
|
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningOrderNotConfirmed DEV dont send sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
|
|
}
|
|
}
|
|
|
|
|
|
public function orderMessage($type) {
|
|
|
|
// @TODO: need to combine all this stuff into one streamlined thing
|
|
|
|
if ($type == 'summary' || $type == 'facebook') {
|
|
// does not show duplicate items, configuration, or item count. returns human readable
|
|
$dishes = [];
|
|
foreach ($this->dishes() as $dish) {
|
|
$dishes[$dish->id_dish] = $dish;
|
|
}
|
|
$dishes = array_values($dishes);
|
|
$food = '';
|
|
$c = count($dishes);
|
|
foreach ($dishes as $x => $dish) {
|
|
$food .= ($x != 0 ? ', ' : '') . ($c > 1 && $x == $c-1 ? '& ' : '') . $dish->dish()->name . ($x == $c-1 ? '.' : '');
|
|
}
|
|
return $food;
|
|
}
|
|
|
|
|
|
// everything else
|
|
switch ($type) {
|
|
case 'sms':
|
|
case 'sms-driver':
|
|
case 'web':
|
|
case 'support':
|
|
$with = 'w/';
|
|
$space = ',';
|
|
$group = false;
|
|
$showCount = false;
|
|
break;
|
|
|
|
case 'phone':
|
|
$with = '. ';
|
|
$space = '.';
|
|
$group = false;
|
|
$showCount = true;
|
|
break;
|
|
|
|
case 'summary':
|
|
$with = '';
|
|
$space = '';
|
|
$group = true;
|
|
$showCount = false;
|
|
break;
|
|
|
|
}
|
|
|
|
if ($type == 'phone') {
|
|
$pFind = ['/fries/i','/BBQ/i'];
|
|
$pReplace = ['frys','barbecue'];
|
|
} else {
|
|
$pFind = $pReplace = [];
|
|
}
|
|
|
|
$i = 1;
|
|
$d = new DateTime('01-01-2000');
|
|
|
|
foreach ($this->dishes() as $dish) {
|
|
|
|
if ($type == 'phone') {
|
|
$prefix = $d->format('jS').' item. ';
|
|
$d->modify('+1 day');
|
|
}
|
|
|
|
$foodItem = "\n- ".$prefix.preg_replace($pFind, $pReplace, $dish->dish()->name);
|
|
$options = $dish->options();
|
|
|
|
if (gettype($options) == 'array') {
|
|
$options = i::o($options);
|
|
}
|
|
|
|
// driver dumbphone tweaks #2673
|
|
if( $type == 'sms-driver' ){
|
|
$withOptions = '';
|
|
$selectOptions = '';
|
|
|
|
if ($options->count()) {
|
|
|
|
foreach ($dish->options() as $option) {
|
|
if ($option->option()->type == 'select') {
|
|
continue;
|
|
}
|
|
|
|
if($option->option()->id_option_parent) {
|
|
$optionGroup = Crunchbutton_Option::o($option->option()->id_option_parent);
|
|
$selectOptions .= trim( $optionGroup->name ) . ': ';
|
|
$selectOptions .= trim( $option->option()->name ) . ', ';
|
|
} else {
|
|
if( $withOptions == '' ){
|
|
$withOptions .= 'With: ';
|
|
}
|
|
$withOptions .= trim( $option->option()->name ) . ', ';
|
|
}
|
|
}
|
|
$withOptions = substr( $withOptions, 0, -2 );
|
|
$selectOptions = substr( $selectOptions, 0, -2 );
|
|
}
|
|
$withoutDefaultOptions = '';
|
|
if( $dish->id_order_dish && $dish->id_dish ){
|
|
$optionsNotChoosen = $dish->optionsDefaultNotChoosen();
|
|
$commas = ' ';
|
|
if( $optionsNotChoosen->count() ){
|
|
foreach( $optionsNotChoosen as $dish_option ){
|
|
$withoutDefaultOptions .= $commas . 'No ' . trim( $dish_option->option()->name );
|
|
$commas = ', ';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $withOptions != '' || $withoutDefaultOptions != '' || $selectOptions != '' ) {
|
|
$foodItem .= ': ';
|
|
}
|
|
|
|
if( $withOptions != '' ){
|
|
$withOptions .= '. ';
|
|
}
|
|
|
|
if( $withoutDefaultOptions != '' ){
|
|
$withoutDefaultOptions .= '. ';
|
|
}
|
|
|
|
if( $selectOptions != '' ){
|
|
$selectOptions .= '. ';
|
|
}
|
|
|
|
$foodItem .= $withoutDefaultOptions;
|
|
$foodItem .= $withOptions;
|
|
$foodItem .= $selectOptions;
|
|
} else {
|
|
if ($options->count()) {
|
|
$foodItem .= ' '.$with.' ';
|
|
|
|
foreach ($dish->options() as $option) {
|
|
if ($option->option()->type == 'select') {
|
|
continue;
|
|
}
|
|
$foodItem .= preg_replace($pFind, $pReplace, $option->option()->name).$space.' ';
|
|
}
|
|
$foodItem = substr($foodItem, 0, -2);
|
|
}
|
|
|
|
$withoutDefaultOptions = '';
|
|
|
|
if( $dish->id_order_dish && $dish->id_dish ){
|
|
$optionsNotChoosen = $dish->optionsDefaultNotChoosen();
|
|
$commas = ' ';
|
|
if( $optionsNotChoosen->count() ){
|
|
foreach( $optionsNotChoosen as $dish_option ){
|
|
$withoutDefaultOptions .= $commas . 'No ' . $dish_option->option()->name;
|
|
$commas = $space . ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $options->count() && $withoutDefaultOptions != '' ) {
|
|
$foodItem .= $space;
|
|
}
|
|
|
|
$withoutDefaultOptions .= '.';
|
|
$foodItem .= $withoutDefaultOptions;
|
|
}
|
|
|
|
if ($type == 'phone') {
|
|
$foodItem .= ']]></Say><Pause length="2" /><Say voice="'.c::config()->twilio->voice.'"><![CDATA[';
|
|
}
|
|
$food .= $foodItem;
|
|
}
|
|
|
|
return $food;
|
|
}
|
|
|
|
public function streetName() {
|
|
$name = explode("\n",$this->address);
|
|
$name = preg_replace('/^[0-9]+ (.*)$/i','\\1',$name[0]);
|
|
$spaceName = '';
|
|
|
|
for ($x=0; $x<strlen($name); $x++) {
|
|
$letter = strtolower($name{$x});
|
|
switch ($letter) {
|
|
case ' ':
|
|
case '.':
|
|
case ',':
|
|
case "\n":
|
|
$addPause = true;
|
|
break;
|
|
case 'c':
|
|
$letter = 'see.';
|
|
default:
|
|
if ($addPause) {
|
|
$spaceName .= '<Pause length="1" />';
|
|
}
|
|
$spaceName .= '<Say voice="'.c::config()->twilio->voice.'"><![CDATA['.$letter.']]></Say><Pause length="1" />';
|
|
$addPause = false;
|
|
break;
|
|
}
|
|
|
|
}
|
|
return $spaceName;
|
|
}
|
|
|
|
public function phoneticStreet($st) {
|
|
$pFind = ['/(st\.)|( st($|\n))/i','/(ct\.)|( ct($|\n))/i','/(ave\.)|( ave($|\n))/i'];
|
|
$pReplace = [' street. ',' court. ',' avenue. '];
|
|
$st = preg_replace($pFind,$pReplace,$st);
|
|
return $st;
|
|
}
|
|
|
|
/**
|
|
* Generates the message to be send in the notification
|
|
*
|
|
* @param string $type What kind of message will be send,
|
|
*
|
|
* @return string
|
|
*/
|
|
public function message($type, $timezone = false) {
|
|
|
|
$food = $this->orderMessage($type);
|
|
|
|
/**
|
|
* Not used anymore but could in the future, so, I'm leaving it here
|
|
* @var string
|
|
*/
|
|
$supportPhone = Cana::config()->phone->support;
|
|
|
|
switch ($type) {
|
|
case 'selfsms':
|
|
$msg = "Crunchbutton.com #".$this->id_order."\n\n";
|
|
$msg .= "Order confirmed!\n\n";
|
|
// #2416
|
|
if( !$this->delivery_service ){
|
|
$msg .= "Restaurant Phone: ".$this->restaurant()->phone().".\n";
|
|
}
|
|
|
|
// #3925
|
|
if( !$this->restaurant()->formal_relationship ) {
|
|
$msg .= "DO NOT call the restaurant. If you have any questions about your order, text or call us back directly!\n";
|
|
$msg .= "\n";
|
|
} else {
|
|
// Removed the delivery estimate #3925
|
|
if( $this->preordered && $this->date_delivery ){
|
|
|
|
$date_delivery = new DateTime( $this->date_delivery, new DateTimeZone( c::config()->timezone ) );
|
|
$date_delivery->setTimezone( new DateTimeZone( $this->restaurant()->timezone ) );
|
|
$msg .= "Your order will arrive around ";
|
|
$msg .= $date_delivery->format('h:i a');
|
|
$msg .= "!\n\n";
|
|
} else{
|
|
if ( $this->delivery_type == 'delivery' && $this->restaurant()->delivery_estimated_time ) {
|
|
$msg .= "Your order will arrive around ";
|
|
$msg .= $this->restaurant()->calc_delivery_estimated_time();
|
|
$msg .= "!\n\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!Order::hasPlacedPreOrderByPhone($this->phone)){
|
|
$msg .= "Next time, try scheduling a pre-order for delivery whatever time you want!\n\n";
|
|
}
|
|
|
|
|
|
$msg .= "To contact Crunchbutton, text us back.\n\n";
|
|
if ($this->pay_type == self::PAY_TYPE_CASH) {
|
|
$msg .= "Remember to tip!\n\n";
|
|
}
|
|
break;
|
|
|
|
case 'support':
|
|
|
|
$date = $this->date();
|
|
|
|
if( $timezone ){
|
|
$date->setTimeZone( new DateTimeZone( $timezone ) );
|
|
}
|
|
|
|
$when = $date->format('M j, g:i a T');
|
|
|
|
$confirmed = $this->confirmed? 'yes' : 'no';
|
|
$refunded = $this->refunded? 'yes':'no';
|
|
|
|
$msg = "
|
|
$this->delivery_type / $this->pay_type, $when
|
|
<br>name: $this->name
|
|
<br>phone: ".Crunchbutton_Util::format_phone($this->phone)."
|
|
<br>confirmed: $confirmed
|
|
<br>refunded: $refunded
|
|
<br><br>food: $food
|
|
";
|
|
if ($this->delivery_type == 'delivery') {
|
|
$msg .= "<br>address: ".$this->address;
|
|
}
|
|
if ($this->notes) {
|
|
$msg .= "<br>notes: ".$this->notes;
|
|
}
|
|
if ($this->pay_type == 'card' && $this->tip) {
|
|
$msg .= "<br>tip: $".$this->tip();
|
|
}
|
|
break;
|
|
|
|
case 'sms':
|
|
$msg = "Crunchbutton #".$this->id_order." \n\n";
|
|
$msg .= $this->name.' ordered '.$this->delivery_type.' paying by '.$this->pay_type.". \n".$food." \n\nphone: ".preg_replace('/[^\d.]/','',$this->phone).'.';
|
|
if ($this->delivery_type == 'delivery') {
|
|
$msg .= " \naddress: ".$this->address;
|
|
}
|
|
if ($this->notes) {
|
|
$msg .= " \nNOTES: ".$this->notes;
|
|
}
|
|
if ($this->pay_type == 'card' && $this->tip) {
|
|
$msg .= " \nTIP: $".$this->tip();
|
|
}
|
|
break;
|
|
|
|
case 'text-drivers':
|
|
$msg = "Order #{$this->id_order}. {$this->restaurant()->name}. " . ( $this->pay_type == 'card' ? 'Credit' : 'Cash' ) . ". ";
|
|
$dishes = explode("\n", $food);
|
|
$_food = '';
|
|
foreach($dishes as $dish){
|
|
$dish = str_replace('- ', '', $dish);
|
|
$dish = str_replace('.', '', $dish);
|
|
$dish = trim($dish);
|
|
$dish = str_replace(' ', ': ', $dish);
|
|
if($dish){
|
|
$_food .= '{{' . $dish . '}} ';
|
|
}
|
|
}
|
|
$msg .= $_food . ". ";
|
|
$msg .= "Subtotal $" . number_format($this->price, 2) . ".";
|
|
if ($this->pay_type == 'card' && $this->tip) {
|
|
$msg .= " Tip: $".$this->tip();
|
|
$msg .= "(" . number_format( $this->tip() / $this->price * 100, 2 ) . "%).";
|
|
}
|
|
$msg .= "\n\nOrder #{$this->id_order}. {$this->name}. {$this->phone}. {$this->address}.";
|
|
if ($this->notes) {
|
|
$msg .= " \nNOTES: ".$this->notes;
|
|
}
|
|
break;
|
|
|
|
case 'sms-driver-priority':
|
|
$spacer = '/';
|
|
$msg = $this->user()->nameAbbr() . "\n" . strtoupper( $this->pay_type ) . $spacer . $this->restaurant()->name . $spacer . $this->driverInstructionsFoodStatus() . $spacer . $this->driverInstructionsPaymentStatus();
|
|
break;
|
|
|
|
case 'sms-driver':
|
|
$spacer = ' / ';
|
|
$msg = "Crunchbutton #".$this->id_order." \n\n";
|
|
$msg .= $this->name.' ordered paying by '.$this->pay_type.". \n".$food." \n\nphone: ".preg_replace('/[^\d.]/','',$this->phone).'.';
|
|
if ($this->delivery_type == 'delivery') {
|
|
$msg .= " \naddress: ".$this->address;
|
|
}
|
|
if ($this->notes) {
|
|
$msg .= " \nNOTES: ".$this->notes;
|
|
}
|
|
$msg .= " \n\nRestaurant: {$this->restaurant()->name} / {$this->restaurant()->phone}";
|
|
$msg .= " \n\n";
|
|
// Payment is card and user tipped
|
|
if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && $this->tip ){
|
|
// remove the tip amount from the notification SMS sent to drivers #5418
|
|
// $msg .= 'TIP ' . $this->tip() . $spacer;
|
|
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && !$this->tip ){
|
|
$msg .= 'TIP BY CASH' . $spacer;
|
|
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CASH ){
|
|
// Driver Text Bug-delete $ amt from cash text msg #3552
|
|
// $msg .= 'TOTAL ' . $this->final_price . $spacer;
|
|
}
|
|
$msg .= $this->driverInstructionsFoodStatus() . $spacer . $this->driverInstructionsPaymentStatus();
|
|
break;
|
|
|
|
case 'phone':
|
|
$spacedPhone = preg_replace('/[^\d.]/','',$this->phone);
|
|
for ($x=0; $x<strlen($spacedPhone); $x++) {
|
|
$spacedPhones .= $spacedPhone{$x}.'. ';
|
|
}
|
|
$msg =
|
|
'Customer Phone number. '.$spacedPhones.'.'
|
|
.'</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'"><![CDATA[Customer Name. '.$this->name.'.]]></Say><Pause length="1" /><Say>';
|
|
|
|
if ($this->delivery_type == 'delivery') {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'"><![CDATA[Address '.$this->phoneticStreet($this->address).'.]]>';
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">This order is for pickup. ';
|
|
}
|
|
|
|
$msg .= '</Say><Pause length="2" /><Say voice="'.c::config()->twilio->voice.'"><![CDATA['.$food.'.]]>';
|
|
|
|
if ($this->notes) {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'"><![CDATA[Customer Notes. '.$this->notes.'.]]>';
|
|
}
|
|
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">Order total: '.$this->phoeneticNumber($this->final_price);
|
|
|
|
if ($this->pay_type == 'card' && $this->tip ) {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">A tip of '.$this->phoeneticNumber($this->tip()).' has been charged to the customer\'s credit card.';
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer will be paying the tip . by cash.';
|
|
}
|
|
|
|
if ( $this->pay_type == 'card') {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer has already paid for this order by credit card.';
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer will pay for this order with cash.';
|
|
}
|
|
|
|
break;
|
|
case 'sms-admin':
|
|
|
|
if( $this->type == 'restaurant' ){
|
|
$spacer = ' / ';
|
|
$msg = $this->name . $spacer . strtoupper( $this->pay_type ) . $spacer . preg_replace( '/[^\d.]/', '', $this->phone ) . $spacer;
|
|
if( $this->delivery_type == Crunchbutton_Order::SHIPPING_DELIVERY ){
|
|
$msg .= $this->address . $spacer;
|
|
}
|
|
$msg .= $this->restaurant()->name . $spacer ;
|
|
if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CASH ){
|
|
$msg .= strtoupper( 'Charge Customer $' . $this->final_price_plus_delivery_markup );
|
|
$msg .= $spacer;
|
|
$msg .= strtoupper( 'Pay Restaurant $' . $this->final_price );
|
|
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD ){
|
|
$msg .= strtoupper( 'Customer Paid $' . $this->final_price_plus_delivery_markup );
|
|
$msg .= $spacer;
|
|
if( $this->tip ){
|
|
$msg .= 'TIP ' . $this->tip();
|
|
} else {
|
|
$msg .= 'TIP BY CASH';
|
|
}
|
|
}
|
|
} else {
|
|
$spacer = ' / ';
|
|
|
|
if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD ){
|
|
$pay_type = 'CARD';
|
|
if( $this->campus_cash ){
|
|
$pay_type = strtoupper( $this->campusCashName() );
|
|
}
|
|
} else {
|
|
$pay_type = 'CASH';
|
|
}
|
|
|
|
$msg = $this->name . $spacer . $pay_type . $spacer . preg_replace( '/[^\d.]/', '', $this->phone ) . $spacer;
|
|
|
|
if( $this->delivery_type == Crunchbutton_Order::SHIPPING_DELIVERY ){
|
|
$msg .= $this->address . $spacer;
|
|
}
|
|
|
|
$msg .= $this->restaurant()->name ;
|
|
|
|
// Payment is card and user tipped
|
|
if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && $this->tip ){
|
|
// Tip should not be in text to drivers #6351
|
|
// $msg .= 'TIP ' . $this->tip();
|
|
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && !$this->tip ){
|
|
$msg .= $spacer . 'TIP BY CASH';
|
|
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CASH ){
|
|
$msg .= $spacer . 'TOTAL ' . $this->final_price_plus_delivery_markup;
|
|
}
|
|
|
|
$msg .= $spacer . $this->driverInstructionsFoodStatus() . $spacer . $this->driverInstructionsPaymentStatus();
|
|
|
|
if( $this->campus_cash ){
|
|
$msg .= $spacer . 'Check ID at delivery';
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
return $msg;
|
|
}
|
|
|
|
public function preOrderDeliveryWindow(){
|
|
if( $this->preordered ){
|
|
$msg = '';
|
|
$date_delivery = new DateTime( $this->date_delivery, new DateTimeZone( c::config()->timezone ) );
|
|
$date_delivery->setTimezone( new DateTimeZone( $this->restaurant()->timezone ) );
|
|
if( $date_delivery->format( 'i' ) > 0 ){
|
|
$msg .= $date_delivery->format( 'g:i' );
|
|
} else {
|
|
$msg .= $date_delivery->format( 'g' );
|
|
}
|
|
$msg .= '-';
|
|
$date_delivery->modify( self::PRE_ORDER_DELIVERY_WINDOW );
|
|
if( $date_delivery->format( 'i' ) > 0 ){
|
|
$msg .= $date_delivery->format( 'g:iA' );
|
|
} else {
|
|
$msg .= $date_delivery->format( 'gA' );
|
|
}
|
|
return $msg;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function phoeneticNumber($num) {
|
|
$num = explode('.',$num);
|
|
return $num[0].' dollar'.($num[0] == 1 ? '' : 's').' and '.$num[1].' cent'.($num[1] == '1' ? '' : 's');
|
|
}
|
|
|
|
public function exports( $params = [] ) {
|
|
|
|
$out = $this->properties();
|
|
|
|
$_ignore = [];
|
|
|
|
if( isset( $params[ 'ignore' ] ) ){
|
|
foreach( $params[ 'ignore' ] as $key => $val ){
|
|
$_ignore[ $val ] = true;
|
|
}
|
|
}
|
|
|
|
unset($out['id_user']);
|
|
unset($out['id']);
|
|
unset($out['id_order']);
|
|
unset($out['delivery_service_markup']);
|
|
unset($out['delivery_service_markup_value']);
|
|
unset($out['txn']);
|
|
|
|
$out['status'] = $status = $this->status()->last();
|
|
|
|
$out['id'] = $this->uuid;
|
|
|
|
if( $out[ 'price_plus_delivery_markup' ] && floatval( $out[ 'price_plus_delivery_markup' ] ) > 0 ){
|
|
$out[ 'price' ] = $out[ 'price_plus_delivery_markup' ];
|
|
}
|
|
|
|
if( $out[ 'final_price_plus_delivery_markup' ] && floatval( $out[ 'final_price_plus_delivery_markup' ] ) > 0 ){
|
|
$out[ 'final_price' ] = $out[ 'final_price_plus_delivery_markup' ];
|
|
}
|
|
|
|
unset( $out[ 'price_plus_delivery_markup' ] );
|
|
unset( $out[ 'final_price_plus_delivery_markup' ] );
|
|
|
|
if( isset( $out[ 'type' ] ) && $out[ 'type' ] == 'compressed' ){
|
|
$out['_restaurant_name'] = $out['restaurant_name'];
|
|
$out['_restaurant_permalink'] = $out['restaurant_permalink'];
|
|
$timezone = new DateTimeZone( $out['timezone'] );
|
|
unset( $out['uuid'] );
|
|
unset( $out['restaurant_name'] );
|
|
unset( $out['restaurant_permalink'] );
|
|
} else {
|
|
|
|
$date = $this->date();
|
|
$out['date_formated'] = $date->format( 'g:i a, M dS, Y' );
|
|
$out['_restaurant_name'] = $this->restaurant()->name;
|
|
$out['_restaurant_permalink'] = $this->restaurant()->permalink;
|
|
$out['_restaurant_phone'] = $this->restaurant()->phone;
|
|
$out['_restaurant_lat'] = $this->restaurant()->loc_lat;
|
|
$out['_restaurant_lon'] = $this->restaurant()->loc_long;
|
|
$out['_restaurant_address'] = $this->restaurant()->address;
|
|
$out['_restaurant_delivery_estimated_time'] = $this->restaurant()->delivery_estimated_time;
|
|
$out['_restaurant_pickup_estimated_time'] = $this->restaurant()->pickup_estimated_time;
|
|
$calc_delivery_estimated_time = $this->restaurant()->calc_delivery_estimated_time( $date->format( 'Y-m-d H:i:s' ), true );
|
|
$out['_restaurant_delivery_estimated_time_formated'] = $calc_delivery_estimated_time->format( 'g:i a' );
|
|
$calc_pickup_estimated_time = $this->restaurant()->calc_pickup_estimated_time( $date->format( 'Y-m-d H:i:s' ), true );
|
|
$out['_restaurant_pickup_estimated_time_formated'] = $calc_pickup_estimated_time->format( 'g:i a' );
|
|
$out['user'] = $this->user()->uuid;
|
|
$out['_message'] = nl2br($this->orderMessage('web'));
|
|
$out['charged'] = $this->charged();
|
|
$credit = $this->chargedByCredit();
|
|
if( $credit > 0 ){
|
|
$out['credit'] = $credit;
|
|
} else {
|
|
$out['credit'] = 0;
|
|
}
|
|
$timezone = new DateTimeZone($this->restaurant()->timezone);
|
|
}
|
|
|
|
$paymentType = $this->paymentType();
|
|
if( $paymentType->id_user_payment_type ){
|
|
$out['card_ending'] = substr( $paymentType->card, -4, 4 );
|
|
} else {
|
|
$out['card_ending'] = false;
|
|
}
|
|
|
|
if( $paymentType->card_type == Crunchbutton_User_Payment_Type::CARD_TYPE_CAMPUS_CASH ){
|
|
$out['card_ending'] = false;
|
|
$out['campus_cash'] = true;
|
|
$out['campus_cash_name'] = $this->campusCashName();
|
|
$out['campus_cash_receipt_info'] = $this->campusCashReceiptInfo();
|
|
}
|
|
|
|
if( $this->date ){
|
|
$date = new DateTime( $this->date, new DateTimeZone( c::config()->timezone ) );
|
|
if( $this->compressed ){
|
|
$date->setTimezone( new DateTimeZone( $this->restaurant()->timezone ) );
|
|
$out[ 'date' ] = $date->format( 'Y-m-d H:i:s' );
|
|
}
|
|
} else if( $this->preordered && $this->preordered_date ){
|
|
$date = new DateTime( $this->preordered_date, new DateTimeZone( c::config()->timezone ) );
|
|
$out['date'] = $this->preordered_date;
|
|
|
|
if( $this->compressed ){
|
|
$date->setTimezone( new DateTimeZone( $this->restaurant()->timezone ) );
|
|
$out[ 'date' ] = $date->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
$date_delivery = new DateTime( $this->date_delivery, new DateTimeZone( c::config()->timezone ) );
|
|
$date_delivery->setTimezone( new DateTimeZone( $this->restaurant()->timezone ) );
|
|
|
|
$out['date_delivery_formatted'] = $date_delivery->format( 'D M d ') . $this->preOrderDeliveryWindow();
|
|
}
|
|
|
|
$date->setTimeZone($timezone);
|
|
|
|
$out['_date_tz'] = $date->format('Y-m-d H:i:s');
|
|
$out['_tz'] = $date->format('T');
|
|
$out['id'] = $this->id_order;
|
|
|
|
$out['summary'] = $this->orderMessage('summary');
|
|
$out['user_has_auth'] = User_Auth::userHasAuth( $this->id_user );
|
|
|
|
$credit = Crunchbutton_Credit::q( 'SELECT * FROM credit c WHERE c.id_order = ? AND c.type = ? AND credit_type = ? LIMIT 1', [$this->id_order, Crunchbutton_Credit::TYPE_CREDIT, Crunchbutton_Credit::CREDIT_TYPE_POINT]);
|
|
if( $out['user_has_auth'] ){
|
|
$credit = Crunchbutton_Credit::q( 'SELECT * FROM credit c WHERE c.id_order = ? AND c.type = ? AND credit_type = ? LIMIT 1', [$this->id_order, Crunchbutton_Credit::TYPE_CREDIT, Crunchbutton_Credit::CREDIT_TYPE_POINT]);
|
|
if( $credit->id_credit ){
|
|
$reward = new Crunchbutton_Reward;
|
|
$points = $reward->processOrder( $this->id_order );
|
|
$sharedTwitter = $reward->orderWasAlreadySharedTwitter( $this->id_order );
|
|
$sharedFacebook = $reward->orderWasAlreadySharedFacebook( $this->id_order );
|
|
$out['reward'] = array( 'points' => Crunchbutton_Credit::formatPoints( $points ), 'shared' => [ 'twitter' => $sharedTwitter, 'facebook' => $sharedFacebook ] );
|
|
}
|
|
} else {
|
|
$reward = new Crunchbutton_Reward;
|
|
$points = $reward->processOrder( $this->id_order );
|
|
$out['reward'] = array( 'points' => Crunchbutton_Credit::formatPoints( $points ) );
|
|
}
|
|
|
|
if( $this->compressed ){
|
|
unset( $out[ 'compressed' ] );
|
|
unset( $out[ 'type' ] );
|
|
unset( $out[ 'id_agent' ] );
|
|
unset( $out[ 'id_restaurant' ] );
|
|
unset( $out[ 'confirmed' ] );
|
|
unset( $out[ 'id_community' ] );
|
|
unset( $out[ 'pay_if_refunded' ] );
|
|
unset( $out[ 'paid_with_cb_card' ] );
|
|
unset( $out[ 'preordered' ] );
|
|
unset( $out[ 'refunded' ] );
|
|
unset( $out[ 'confirmed' ] );
|
|
unset( $out[ 'preordered_date' ] );
|
|
unset( $out[ 'asked_to_call' ] );
|
|
unset( $out[ 'reimburse_cash_order' ] );
|
|
unset( $out[ 'do_not_pay_restaurant' ] );
|
|
unset( $out[ 'do_not_pay_driver' ] );
|
|
unset( $out[ 'likely_test' ] );
|
|
unset( $out[ 'geomatched' ] );
|
|
unset( $out[ 'id_phone' ] );
|
|
unset( $out[ 'campus_cash' ] );
|
|
unset( $out[ 'preorder_processed' ] );
|
|
unset( $out[ 'card_ending' ] );
|
|
unset( $out[ 'do_not_reimburse_driver' ] );
|
|
unset( $out[ 'id_user_payment_type' ] );
|
|
unset( $out[ 'reward_delivery_free' ] );
|
|
unset( $out[ 'delivery_status' ] );
|
|
unset( $out[ 'id_address' ] );
|
|
unset( $out[ 'delivery_service' ] );
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
public function paymentType(){
|
|
if( $this->id_user_payment_type ){
|
|
return Crunchbutton_User_Payment_Type::o( $this->id_user_payment_type );
|
|
}
|
|
}
|
|
|
|
public function refundGiftFromOrder(){
|
|
if( $this->chargedByCredit() ){
|
|
$credits = Crunchbutton_Credit::creditByOrder( $this->id_order );
|
|
if( $credits->count() > 0 ){
|
|
foreach( $credits as $credit ){
|
|
|
|
// We want just the debits
|
|
if( $credit->type != Crunchbutton_Credit::TYPE_DEBIT ){
|
|
continue;
|
|
}
|
|
// Creates a new credit to the user
|
|
$creditRefounded = new Crunchbutton_Credit();
|
|
$creditRefounded->id_user = $credit->id_user;
|
|
$creditRefounded->type = Crunchbutton_Credit::TYPE_CREDIT;
|
|
// $creditRefounded->id_restaurant = $this->id_restaurant;
|
|
$creditRefounded->date = date('Y-m-d H:i:s');
|
|
$creditRefounded->value = $credit->value;
|
|
$creditRefounded->id_order_reference = $this->id_order;
|
|
$creditRefounded->id_restaurant_paid_by = $this->id_restaurant_paid_by;
|
|
$creditRefounded->paid_by = $this->paid_by;
|
|
$creditRefounded->credit_type = Crunchbutton_Credit::CREDIT_TYPE_CASH;
|
|
$creditRefounded->note = 'Value ' . $credit->value . ' refunded from order: ' . $this->id_order . ' - ' . date('Y-m-d H:i:s');
|
|
$creditRefounded->save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function refundedAmount($ch) {
|
|
if (!isset($this->_refundedAmount)) {
|
|
$this->_refundedAmount = 0;
|
|
foreach ($ch->refunds as $refund) {
|
|
$this->_refundedAmount += $refund->amount;
|
|
}
|
|
}
|
|
return $this->_refundedAmount;
|
|
}
|
|
|
|
public function tellDriverTheOrderWasCanceled(){
|
|
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND ( type = ? OR type = ? OR type = ? ) ORDER BY id_order_action DESC LIMIT 1 ', [ $this->id_order, Crunchbutton_Order_Action::DELIVERY_ACCEPTED, Crunchbutton_Order_Action::DELIVERY_PICKEDUP, Crunchbutton_Order_Action::DELIVERY_TRANSFERED ] )->get( 0 );
|
|
|
|
if( $action->id_admin ){
|
|
|
|
$driver = Admin::o( $action->id_admin );
|
|
|
|
$sendMessageToDriver = true;
|
|
|
|
$status = $this->status();
|
|
|
|
if( $status ){
|
|
$last = $status->last();
|
|
if( $last[ 'status' ] == 'delivered' ){
|
|
$sendMessageToDriver = false;
|
|
}
|
|
}
|
|
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND type = ? ORDER BY id_order_action DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Action::TICKET_DO_NOT_DELIVERY ] )->get( 0 );
|
|
|
|
if( !$action->id_order && $sendMessageToDriver ){
|
|
$message = "System notification: Sorry, " . $this->restaurant()->name . " order #" . $this->id_order . " from " . $this->name . " was just canceled. Please don't deliver it!";
|
|
Crunchbutton_Support::createNewWarning( [ 'body' => $message, 'phone' => $driver->phone, 'dont_open_ticket' => true ] );
|
|
Crunchbutton_Message_Sms::send( [ 'to' => $driver->phone, 'message' => $message, 'reason' => Crunchbutton_Message_Sms::REASON_DRIVER_ORDER_CANCELED ] );
|
|
$action = new Crunchbutton_Order_Action;
|
|
$action->id_order = $this->id_order;
|
|
$action->timestamp = date( 'Y-m-d H:i:s' );
|
|
$action->type = Crunchbutton_Order_Action::TICKET_DO_NOT_DELIVERY;
|
|
$action->save();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public function refund($amt, $note = null, $tell_driver = false, $id_admin = null, $que = true, $tell_customer = false) {
|
|
|
|
if( $que ){
|
|
// check if the order really was refunded
|
|
$refunded = Crunchbutton_Order_Transaction::q( 'SELECT * FROM order_transaction WHERE id_order = ? AND type = ? ORDER BY id_order_transaction DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Transaction::TYPE_REFUNDED ] )->get( 0 );
|
|
if( $refunded->id_order_transaction ){
|
|
return;
|
|
} else {
|
|
$this->refunded = 0;
|
|
$this->save();
|
|
}
|
|
}
|
|
|
|
if (!$this->refunded){
|
|
|
|
if( $tell_driver ){
|
|
$this->tellDriverTheOrderWasCanceled();
|
|
}
|
|
|
|
if($tell_customer){
|
|
$this->tellCustomerTheOrderWasCanceled();
|
|
}
|
|
|
|
// Refund the gift
|
|
$this->refundGiftFromOrder();
|
|
|
|
if ( intval( $this->charged() ) > 0 ) {
|
|
|
|
$paymentType = $this->paymentType();
|
|
|
|
if ($this->pay_type == self::PAY_TYPE_CREDIT_CARD && $paymentType->card_type != Crunchbutton_User_Payment_Type::CARD_TYPE_CAMPUS_CASH) {
|
|
|
|
switch ($this->processor) {
|
|
case 'stripe':
|
|
default:
|
|
if( floatval( $amt ) > 0 ){
|
|
try {
|
|
$params = $amt ? ['amount' => $amt * 100] : null;
|
|
$ch = \Stripe\Charge::retrieve($this->txn);
|
|
$re = $ch->refunds->create( $params );
|
|
|
|
} catch (Exception $e) {
|
|
echo $e->getMessage();
|
|
return (object)['status' => false, 'errors' => $e->getMessage()];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$support = $this->getSupport();
|
|
if ($support) {
|
|
$support->addSystemMessage('Order refunded.');
|
|
}
|
|
|
|
$this->refunded = 1;
|
|
$this->save();
|
|
|
|
$id_admin = $id_admin ? $id_admin : c::user()->id_admin;
|
|
|
|
// saves an order transaction
|
|
$transaction = new Crunchbutton_Order_Transaction;
|
|
// needs to be changed when we start to do partial refund
|
|
$transaction->amt = $amt ? $amt : $this->charged();
|
|
$transaction->type = Crunchbutton_Order_Transaction::TYPE_REFUNDED;
|
|
$transaction->date = date( 'Y-m-d H:i:s' );;
|
|
$transaction->note = $note;
|
|
$transaction->id_order = $this->id_order;
|
|
$transaction->id_user_payment_type = $this->id_user_payment_type;
|
|
$transaction->source = Crunchbutton_Order_Transaction::SOURCE_CRUNCHBUTTON;
|
|
$transaction->id_admin = $id_admin;
|
|
$transaction->save();
|
|
|
|
return (object)['status' => true];
|
|
}
|
|
return (object)['status' => false];
|
|
}
|
|
|
|
public function getSupport($create = false) {
|
|
$support = Support::getSupportForOrder($this->id_order);
|
|
if (!$support && $create) {
|
|
$support = Crunchbutton_Support::createNewTicket([
|
|
'id_order' => $this->id_order,
|
|
'body' => 'Ticket created from admin panel.'
|
|
]);
|
|
}
|
|
return $support;
|
|
}
|
|
|
|
public function refundedStatus(){
|
|
if( $this->refunded ){
|
|
$transaction = $this->refundedReason();
|
|
if(!$transaction){
|
|
return self::STATUS_REFUNDED_PROCESSING;
|
|
}
|
|
if( $transaction->amt === $this->charged() ){
|
|
return self::STATUS_REFUNDED_TOTAL;
|
|
} else {
|
|
return self::STATUS_REFUNDED_PARTIALLY;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function refundedTotal(){
|
|
if( $this->refunded ){
|
|
$transaction = $this->refundedReason();
|
|
return floatval( $transaction->amt );
|
|
}
|
|
}
|
|
|
|
public function refundedReason(){
|
|
if( $this->refunded ){
|
|
$transaction = Crunchbutton_Order_Transaction::getRefundedReason( $this->id_order );
|
|
if( $transaction->id_order_transaction ){
|
|
return $transaction;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function phone() {
|
|
$phone = $this->phone;
|
|
$phone = preg_replace('/[^\d]*/i','',$phone);
|
|
$phone = preg_replace('/(\d{3})(\d{3})(.*)/', '\\1-\\2-\\3', $phone);
|
|
|
|
return $phone;
|
|
}
|
|
|
|
// Gets the last order tipped by the user
|
|
public static function lastTippedOrder( $id_user = null ) {
|
|
$id_user = ( $id_user ) ? $id_user : $this->id_user;
|
|
return self::q('select * from `order` where id_user=? and tip is not null and tip > 0 order by id_order desc limit 1 offset 0',[$id_user]);
|
|
}
|
|
|
|
public function lastTipByDelivery($id_user = null, $delivery ) {
|
|
$id_user = ( $id_user ) ? $id_user : $this->id_user;
|
|
if( $id_user ){
|
|
$order = self::q('select * from `order` where id_user=? and delivery_type = ? and tip is not null order by id_order desc limit 1 offset 0', [$id_user, $delivery]);
|
|
if( $order->tip ){
|
|
return $order->tip;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static function lastDeliveredOrder($id_user = nul) {
|
|
$id_user = ( $id_user ) ? $id_user : $this->id_user;
|
|
if( $id_user ){
|
|
$order = self::q("SELECT * FROM `order` WHERE id_user = ? AND delivery_type = 'delivery' ORDER BY id_order DESC LIMIT 1", [$id_user]);
|
|
if( $order->id_order ){
|
|
return Order::o( $order->id_order );
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static function lastTip( $id_user = null ) {
|
|
$last_order = self::lastTippedOrder( $id_user );
|
|
if( $last_order->tip ){
|
|
return $last_order->tip;
|
|
}
|
|
return null;
|
|
}
|
|
public static function lastTipType( $id_user = null ) {
|
|
$last_order = self::lastTippedOrder( $id_user );
|
|
if( $last_order->tip_type ){
|
|
return strtolower( $last_order->tip_type );
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function agent() {
|
|
return Agent::o($this->id_agent);
|
|
}
|
|
|
|
public function isNativeApp(){
|
|
$agent = $this->agent();
|
|
if( $agent->id_agent ){
|
|
return $agent->isNativeApp();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function isIPhone(){
|
|
$agent = $this->agent();
|
|
if( $agent->id_agent ){
|
|
return $agent->isIPhone();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function hasUserAlreadyOrderedUsingNativeApp(){
|
|
return Crunchbutton_Agent::hasUserAlreadyOrderedUsingNativeApp( $this->phone );
|
|
}
|
|
|
|
public function wasLinkAlreadySent(){
|
|
return Crunchbutton_Phone_Log::wasAppLinkAlreadySent( $this->phone );
|
|
}
|
|
|
|
// Issue #4262
|
|
public function sendNativeAppLink(){
|
|
if( $this->isIPhone() && !$this->hasUserAlreadyOrderedUsingNativeApp() && !$this->wasLinkAlreadySent() ){
|
|
$message = "Enjoy your food, " . $this->name . ", and, next time, order faster with our app! \nhttp://_DOMAIN_/app";
|
|
$num = $this->phone;
|
|
Crunchbutton_Message_Sms::send( [
|
|
'to' => $num,
|
|
'message' => $message,
|
|
'reason' => Crunchbutton_Message_Sms::REASON_APP_DOWNLOAD
|
|
] );
|
|
} else {
|
|
Log::debug( [ 'action' => 'send native app link', 'phone' => $this->phone(), 'isPhone' => $this->isIPhone(), 'has used native app' => $this->hasUserAlreadyOrderedUsingNativeApp(), 'link already sent' =>$this->wasLinkAlreadySent(), 'type' => 'native app link' ] );
|
|
}
|
|
}
|
|
|
|
public function community() {
|
|
if( !$this->_community ){
|
|
$this->_community = Community::o($this->id_community);;
|
|
}
|
|
return $this->_community;
|
|
}
|
|
|
|
public function hasGiftCard(){
|
|
if( !$this->id_order ){
|
|
return 0;
|
|
}
|
|
$query = 'SELECT SUM( value ) as total FROM promo WHERE id_order_reference = ?';
|
|
$row = Cana::db()->get( $query, [$this->id_order])->get(0);
|
|
if( $row->total ){
|
|
return $row->total;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function hasCredit(){
|
|
$query = 'SELECT SUM( value ) as total FROM credit WHERE id_order_reference = ? AND ( credit_type = ? OR credit_type != ? ) AND id_promo IS NULL';
|
|
$row = Cana::db()->get( $query ,[$this->id_order, Crunchbutton_Credit::CREDIT_TYPE_CASH, Crunchbutton_Credit::CREDIT_TYPE_POINT])->get(0);
|
|
if( $row->total ){
|
|
return $row->total;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function expectedByStealthFax() {
|
|
$date = clone $this->date();
|
|
$date->modify('+ 20 minute');
|
|
return $date;
|
|
}
|
|
|
|
public function expectedBy() {
|
|
|
|
// See #4306
|
|
if( $this->restaurant()->delivery_service ){
|
|
return $this->expectedByStealthFax();
|
|
}
|
|
|
|
$time = clone $this->date();
|
|
$multipleOf = 15;
|
|
$minutes = round( ( ( $time->format( 'i' ) + $this->restaurant()->delivery_estimated_time ) + $multipleOf / 2 ) / $multipleOf ) * $multipleOf;
|
|
$minutes -= $time->format( 'i' );
|
|
$time->modify( '+ ' . $minutes . ' minute' );
|
|
return $time;
|
|
}
|
|
|
|
public static function totalOrdersByPhone( $phone ){
|
|
$phone = Phone::clean( $phone );
|
|
// referral phone test
|
|
if( $phone == '_PHONE_' ){
|
|
return 0;
|
|
}
|
|
$query = 'SELECT COUNT(*) AS total FROM `order` INNER JOIN phone using(id_phone) WHERE phone.phone = ?';
|
|
$row = Cana::db()->get( $query, [$phone])->get(0);
|
|
if( intval( $row->total ) ){
|
|
return intval( $row->total );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public static function hasPlacedPreOrderByPhone( $phone ){
|
|
$phone = Phone::clean( $phone );
|
|
// referral phone test
|
|
if( $phone == '_PHONE_' ){
|
|
return 0;
|
|
}
|
|
$query = 'SELECT COUNT(*) AS total FROM `order` INNER JOIN phone using(id_phone) WHERE phone.phone = ? AND `order`.preordered = 1';
|
|
$row = Cana::db()->get( $query, [$phone])->get(0);
|
|
if( intval( $row->total ) ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static function totalOrdersByCustomer( $id_user ){
|
|
$query = 'SELECT COUNT(*) AS total FROM `order` WHERE id_user = ?';
|
|
$row = Cana::db()->get( $query, [$id_user])->get(0);
|
|
if( $row->total ){
|
|
return $row->total;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function restaurantsUserHasPermissionToSeeTheirOrders(){
|
|
$restaurants_ids = [];
|
|
$_permissions = new Crunchbutton_Admin_Permission();
|
|
$all = $_permissions->all();
|
|
// Get all restaurants permissions
|
|
$restaurant_permissions = $all[ 'order' ][ 'permissions' ];
|
|
$permissions = c::admin()->getAllPermissionsName();
|
|
$restaurants_id = array();
|
|
foreach ( $permissions as $permission ) {
|
|
$permission = $permission->permission;
|
|
$info = $_permissions->getPermissionInfo( $permission );
|
|
$name = $info[ 'permission' ];
|
|
foreach( $restaurant_permissions as $restaurant_permission_name => $meta ){
|
|
if( $restaurant_permission_name == $name ){
|
|
if( strstr( $name, 'ID' ) ){
|
|
$regex = str_replace( 'ID' , '((.)*)', $name );
|
|
$regex = '/' . $regex . '/';
|
|
preg_match( $regex, $permission, $matches );
|
|
if( count( $matches ) > 0 ){
|
|
$restaurants_ids[] = $matches[ 1 ];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return array_unique( $restaurants_ids );
|
|
}
|
|
|
|
public function lastStatus(){
|
|
if( !$this->_last_status ){
|
|
$status = Order_Action::o( $this->delivery_status );
|
|
if( !$status->id_order_action ){
|
|
$status = $this->status()->last();
|
|
} else {
|
|
$status = $status->export2Array();
|
|
}
|
|
$this->_last_status = $status;
|
|
}
|
|
return $this->_last_status;
|
|
}
|
|
|
|
public function status() {
|
|
if (!$this->_statuss) {
|
|
$this->_statuss = new Order_Status($this);
|
|
}
|
|
return $this->_statuss;
|
|
}
|
|
|
|
public function clearStatus(){
|
|
$this->_statuss = null;
|
|
}
|
|
|
|
|
|
public function undoStatus() {
|
|
$status = $this->status()->last();
|
|
$status = 'delivery-'.$status['status'];
|
|
|
|
switch ($status) {
|
|
case Crunchbutton_Order_Action::DELIVERY_NEW:
|
|
case Crunchbutton_Order_Action::DELIVERY_ACCEPTED:
|
|
$newStatus = Crunchbutton_Order_Action::DELIVERY_REJECTED;
|
|
break;
|
|
case Crunchbutton_Order_Action::DELIVERY_PICKEDUP:
|
|
$newStatus = Crunchbutton_Order_Action::DELIVERY_ACCEPTED;
|
|
break;
|
|
case Crunchbutton_Order_Action::DELIVERY_DELIVERED:
|
|
$newStatus = Crunchbutton_Order_Action::DELIVERY_PICKEDUP;
|
|
break;
|
|
case Crunchbutton_Order_Action::DELIVERY_REJECTED:
|
|
$newStatus = Crunchbutton_Order_Action::DELIVERY_NEW;
|
|
break;
|
|
}
|
|
|
|
if (!$newStatus) {
|
|
return false;
|
|
}
|
|
|
|
$this->setStatus($newStatus);
|
|
|
|
return str_replace('delivery-', '', $newStatus);
|
|
}
|
|
|
|
|
|
public function setStatus($status, $notify = false, $admin = null, $note = null, $force = false, $rejectedTicket = true) {
|
|
if (!$status) {
|
|
return false;
|
|
}
|
|
|
|
if (!$admin) {
|
|
$admin = c::user();
|
|
}
|
|
|
|
if( !$force ){
|
|
if ($this->status()->driver() && $this->status()->driver()->id_admin != $admin->id_admin) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$action = new Order_Action([
|
|
'id_order' => $this->id_order,
|
|
'id_admin' => $admin->id_admin,
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'note' => $note,
|
|
'type' => $status
|
|
]);
|
|
$action->save();
|
|
|
|
if ($notify) {
|
|
// Notify customer about their driver
|
|
$q = Queue::create([
|
|
'type' => Crunchbutton_Queue::TYPE_NOTIFICATION_YOUR_DRIVER,
|
|
'id_order' => $this->id_order,
|
|
'seconds' => 5
|
|
]);
|
|
}
|
|
|
|
if($status == Crunchbutton_Order_Action::DELIVERY_ACCEPTED){
|
|
// When an order is accepted before it was processed the system most to mark the order as processed. #7978
|
|
if( $this->preordered && !$this->preorder_processed && !$this->refunded ){
|
|
$status = $this->status()->last();
|
|
if( $status[ 'status' ] == 'new' ){
|
|
$this->que();
|
|
$this->date = date( 'Y-m-d H:i:s' );
|
|
$this->preorder_processed = 1;
|
|
$this->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add/Remove pex card funds
|
|
$q = Queue::create([
|
|
'type' => Crunchbutton_Queue::TYPE_ORDER_PEXCARD_FUNDS,
|
|
'id_order' => $this->id_order,
|
|
'seconds' => 0
|
|
]);
|
|
|
|
if( $status == Crunchbutton_Order_Action::DELIVERY_REJECTED && $rejectedTicket ){
|
|
Order_Action::ticketForRejectedOrder( $this->id_order );
|
|
}
|
|
|
|
if( $status == Crunchbutton_Order_Action::DELIVERY_REJECTED ){
|
|
Crunchbutton_Pexcard_Report_Order::removeByOrder( $this->id_order );
|
|
$this->delivery_status = null;
|
|
} else {
|
|
$this->delivery_status = $action->id_order_action;
|
|
}
|
|
|
|
$this->save();
|
|
|
|
// mark the order to be paid by commission structure
|
|
if( $admin->openedCommunity() && !$this->isForcedToBeCommissioned( $admin->id_admin ) ){
|
|
$this->markToBeCommissioned( $admin->id_admin );
|
|
}
|
|
|
|
if($status == Crunchbutton_Order_Action::DELIVERY_ACCEPTED && $this->orderHasRepsHasFailedTicket()){
|
|
|
|
$message = 'Order #' . $this->id_order . ' already accepted by ' . $admin->name;
|
|
Crunchbutton_Support::createNewWarning( [ 'body' => $message, 'id_order' => $this->id_order ] );
|
|
}
|
|
|
|
if( $status == Crunchbutton_Order_Action::DELIVERY_CANCELED ){
|
|
Crunchbutton_Pexcard_Report_Order::removeByOrder( $this->id_order );
|
|
$this->do_not_reimburse_driver = true;
|
|
$this->do_not_pay_restaurant = true;
|
|
$this->do_not_pay_driver = true;
|
|
$this->save();
|
|
$this->tellDriverTheOrderWasCanceled();
|
|
}
|
|
|
|
if( $status == Crunchbutton_Order_Action::DELIVERY_DELIVERED ){
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$params = [ 'id_order' => $this->id_order,
|
|
'id_admin' => $admin->id_admin,
|
|
'date' => $now->format( 'Y-m-d H:i:s' ),
|
|
'date_formatted' => $now->format( 'M jS Y g:i:s A T' ),
|
|
'type' => $this->pay_type,
|
|
'should_use' => $this->shouldUsePexCard(),
|
|
'amount' => floatval( $this->final_price - $this->delivery_fee ) ];
|
|
Crunchbutton_Pexcard_Report_Order::byOrder( $params );
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
public function markToBeCommissioned( $id_admin ){
|
|
(new Order_Action([
|
|
'id_order' => $this->id_order,
|
|
'id_admin' => $id_admin,
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'type' => Crunchbutton_Order_Action::FORCE_COMMISSION_PAYMENT
|
|
]))->save();
|
|
}
|
|
|
|
public function isForcedToBeCommissioned( $id_admin = false ){
|
|
return Crunchbutton_Order_Action::isForcedToBeCommissioned( $this->id_order, $id_admin );
|
|
}
|
|
|
|
public function pexcardFunds(){
|
|
|
|
$order = Order::o( $this->id_order );
|
|
|
|
$status = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? ORDER BY id_order_action DESC LIMIT 1', [ $order->id_order ] )->get( 0 );
|
|
|
|
if( $status->id_order_action && $status->id_admin ){
|
|
|
|
$driver = Admin::o( $status->id_admin );
|
|
|
|
if( $driver->id_admin ){
|
|
|
|
// Pexcard stuff - #3992
|
|
$pexcard = $driver->pexcard();
|
|
|
|
if( $pexcard->id_admin_pexcard ){
|
|
|
|
$status = $status->type;
|
|
|
|
switch ( $status ) {
|
|
|
|
case Crunchbutton_Order_Action::DELIVERY_ACCEPTED:
|
|
case Crunchbutton_Order_Action::FORCE_COMMISSION_PAYMENT:
|
|
// Add $10 for the first accepted order - #3993
|
|
$shift = Crunchbutton_Community_Shift::shiftDriverIsCurrentWorkingOn( $driver->id_admin );
|
|
|
|
if( $shift->id_admin_shift_assign ){
|
|
$pexcard->addShiftStartFunds( $shift->id_admin_shift_assign );
|
|
}
|
|
|
|
// https://github.com/crunchbutton/crunchbutton/issues/3992#issuecomment-70799809
|
|
$loadCard = true;
|
|
|
|
if( $order->pay_type == 'card' && $order->restaurant()->formal_relationship ){
|
|
$loadCard = false;
|
|
}
|
|
|
|
if( $loadCard ){
|
|
|
|
$pexcard->addFundsOrderAccepeted( $order->id_order );
|
|
|
|
Log::debug([ 'actions' => 'pex card LOADED', 'id_order' => $order->id_order, 'type' => 'pexcard-load' ]);
|
|
} else {
|
|
Log::debug([ 'actions' => 'pex card NOT loaded', 'id_order' => $order->id_order, 'type' => 'pexcard-load' ]);
|
|
|
|
}
|
|
break;
|
|
|
|
case Crunchbutton_Order_Action::DELIVERY_REJECTED:
|
|
|
|
Log::debug([ 'actions' => 'pex card funds REMOVED', 'id_order' => $order->id_order, 'type' => 'pexcard-load' ]);
|
|
|
|
$pexcard->removeFundsOrderRejected( $order->id_order );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function deliveryReply($admin) {
|
|
|
|
$act = false;
|
|
|
|
foreach ($this->_actions as $action) {
|
|
if ($action->id_admin && $admin->id_admin) {
|
|
switch ($action->type) {
|
|
case 'delivery-delivered':
|
|
$act = 'delivered';
|
|
continue;
|
|
break;
|
|
|
|
case 'delivery-pickedup':
|
|
$act = 'pickedup';
|
|
continue;
|
|
break;
|
|
|
|
case 'delivery-accepted':
|
|
$act = 'accepted';
|
|
continue;
|
|
break;
|
|
|
|
case 'delivery-rejected':
|
|
if ( $action->id_admin == $admin->id_admin ) {
|
|
$act = 'rejected';
|
|
}
|
|
continue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return $act;
|
|
}
|
|
|
|
// @legacy: should only be used on cbtn.io
|
|
public function deliveryLastStatus(){
|
|
$statuses = $this->deliveryStatus();
|
|
if( $statuses[ 'delivered' ] ){
|
|
return array( 'status' => 'delivered', 'name' => $statuses[ 'delivered' ]->name, 'id_admin' => $statuses[ 'delivered' ]->id_admin, 'order' => 3, 'date' => $statuses[ 'delivered_date' ], 'timezone' => $this->restaurant()->timezone );
|
|
}
|
|
if( $statuses[ 'pickedup' ] ){
|
|
return array( 'status' => 'pickedup', 'name' => $statuses[ 'pickedup' ]->name, 'id_admin' => $statuses[ 'pickedup' ]->id_admin, 'order' => 2, 'date' => $statuses[ 'pickedup_date' ], 'timezone' => $this->restaurant()->timezone );
|
|
}
|
|
if( $statuses[ 'accepted' ] ){
|
|
return array( 'status' => 'accepted', 'name' => $statuses[ 'accepted' ]->name, 'id_admin' => $statuses[ 'accepted' ]->id_admin, 'order' => 1, 'date' => $statuses[ 'accepted_date' ], 'timezone' => $this->restaurant()->timezone );
|
|
}
|
|
return array ( 'status' => 'new', 'order' => 0 );
|
|
}
|
|
|
|
public function deliveryTimes(){
|
|
$statuses = $this->deliveryStatus();
|
|
if( $statuses[ 'delivered' ] ){
|
|
return array( 'status' => 'delivered', 'name' => $statuses[ 'delivered' ]->name, 'id_admin' => $statuses[ 'delivered' ]->id_admin, 'order' => 3, 'date_pickedup' => $statuses[ 'pickedup_date' ], 'date_delivered' => $statuses[ 'delivered_date' ], 'timezone' => $this->restaurant()->timezone );
|
|
}
|
|
if( $statuses[ 'pickedup' ] ){
|
|
return array( 'status' => 'pickedup', 'name' => $statuses[ 'delivered' ]->name, 'id_admin' => $statuses[ 'delivered' ]->id_admin, 'order' => 2, 'date_pickedup' => $statuses[ 'pickedup_date' ], 'date_delivered' => $statuses[ 'delivered_date' ], 'timezone' => $this->restaurant()->timezone );
|
|
}
|
|
}
|
|
|
|
public function wasAcceptedByRep(){
|
|
$query = "SELECT * FROM
|
|
order_action ac
|
|
WHERE
|
|
ac.id_order = {$this->id_order}
|
|
AND ( ac.type = '" . Crunchbutton_Order_Action::DELIVERY_PICKEDUP . "'
|
|
OR ac.type = '" . Crunchbutton_Order_Action::DELIVERY_ACCEPTED . "'
|
|
OR ac.type = '" . Crunchbutton_Order_Action::DELIVERY_REJECTED . "'
|
|
OR ac.type = '" . Crunchbutton_Order_Action::DELIVERY_DELIVERED . "' )
|
|
ORDER BY id_order_action DESC LIMIT 1";
|
|
$action = Crunchbutton_Order_Action::q( $query );
|
|
if( $action->count() > 0 ){
|
|
$action = $action->get( 0 );
|
|
if( $action->type == Crunchbutton_Order_Action::DELIVERY_REJECTED ){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function wasCanceled(){
|
|
$lastStatus = $this->status()->last();
|
|
if( $lastStatus && $lastStatus[ 'status' ] && $lastStatus[ 'status' ] == 'canceled' ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
public function textCustomerAboutDriver(){
|
|
|
|
$order = Crunchbutton_Order::o( $this->id_order );
|
|
|
|
if( !$order->id_order ){
|
|
return;
|
|
}
|
|
|
|
$this->_actions = false;
|
|
|
|
$phone = $order->phone;
|
|
$driver = $order->getDeliveryDriver();
|
|
|
|
$firstName = Crunchbutton_Message_Sms::greeting( $order->user()->firstName() );
|
|
|
|
if( $driver ){
|
|
// Check if the order was rejected and change the message
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE type = ? AND id_order = ?', [Crunchbutton_Order_Action::DELIVERY_REJECTED, $this->id_order]);
|
|
if( $action->count() > 0 ){
|
|
$message = $firstName . "You've got a new driver! For order updates, text {$driver->nameAbbr()} at {$driver->phone}";
|
|
} else {
|
|
$message = $firstName . "Your driver today is {$driver->nameAbbr()}. For order updates, text {$driver->firstName()} at {$driver->phone}";
|
|
}
|
|
Crunchbutton_Message_Sms::send( [ 'to' => $phone, 'message' => $message, 'reason' => Crunchbutton_Message_Sms::REASON_CUSTOMER_DRIVER ] );
|
|
}
|
|
}
|
|
|
|
public function hasGiftCardIssued(){
|
|
// check if it has a gift card
|
|
$promo = Crunchbutton_Promo::q('SELECT * FROM promo p WHERE p.id_order_reference = ?', [$this->id_order]);
|
|
if( $promo->count() > 0 ){
|
|
return true;
|
|
}
|
|
// check if it has credit
|
|
$credit = Crunchbutton_Credit::q('SELECT * FROM credit c WHERE c.id_order_reference = ? AND ( c.credit_type = ? OR c.credit_type != ? )', [$this->id_order, Crunchbutton_Credit::CREDIT_TYPE_CASH, Crunchbutton_Credit::CREDIT_TYPE_POINT]);
|
|
if( $credit->count() > 0 ){
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
public function getDeliveryDriver(){
|
|
// for payment reasons the driver could be changed at payment time #3232
|
|
// $status = $this->status()->last();
|
|
|
|
$actions = Order_Action::q('
|
|
select * from order_action
|
|
where id_order=?
|
|
and type!=?
|
|
and type!=?
|
|
and type!=?
|
|
and type!=?
|
|
and type!=?
|
|
and type!=?
|
|
and type!=?
|
|
order by id_order_action desc
|
|
', [$this->id_order,
|
|
Crunchbutton_Order_Action::DELIVERY_NEW,
|
|
Crunchbutton_Order_Action::FORCE_COMMISSION_PAYMENT,
|
|
Crunchbutton_Order_Action::TICKET_CAMPUS_CASH,
|
|
Crunchbutton_Order_Action::TICKET_CAMPUS_CASH_REMINDER,
|
|
Crunchbutton_Order_Action::DELIVERY_CANCELED,
|
|
Crunchbutton_Order_Action::TICKET_REPS_FAILED_PICKUP,
|
|
Crunchbutton_Order_Action::TICKET_DO_NOT_DELIVERY ]);
|
|
if($actions->id_admin){
|
|
return Admin::o($actions->id_admin);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function deliveryExports() {
|
|
return [
|
|
'id_order' => $this->id_order,
|
|
'uuid' => $this->uuid,
|
|
'delivery-status' => [
|
|
'delivered' => $this->deliveryStatus('delivery-delivered') ? $this->deliveryStatus('delivery-delivered') : false,
|
|
'pickedup' => $this->deliveryStatus('delivery-pickedup') ? $this->deliveryStatus('delivery-pickedup') : false,
|
|
'accepted' => $this->deliveryStatus('delivery-accepted') ? $this->deliveryStatus('delivery-accepted') : false,
|
|
],
|
|
'self-reply' => $this->deliveryReply(c::admin())
|
|
];
|
|
}
|
|
|
|
public function driverInstructionsPaymentBGColor(){
|
|
// Driver feedback changing color of cash vs pex orders #7135
|
|
if( $this->restaurant()->formal_relationship ){
|
|
if( $this->pay_type == 'cash' ){
|
|
$driver = c::user();
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'green';
|
|
} else {
|
|
return 'green';
|
|
}
|
|
} else {
|
|
return 'red';
|
|
}
|
|
} else {
|
|
$driver = c::user();
|
|
if( $this->pay_type == 'cash' ){
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'green';
|
|
} else {
|
|
return 'green';
|
|
}
|
|
} else {
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'red';
|
|
} else {
|
|
return 'red';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function driverInstructionsPaymentStatus(){
|
|
// Clarify Cash/Credit Orders #4481
|
|
if( $this->restaurant()->formal_relationship ){
|
|
if( $this->pay_type == 'cash' ){
|
|
$driver = c::user();
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'CASH order. Pay restaurant with your own cash, not PEX.';
|
|
} else {
|
|
return 'CASH order. Pay restaurant with your own cash.';
|
|
}
|
|
} else {
|
|
return 'Do not pay the restaurant';
|
|
}
|
|
} else {
|
|
$driver = c::user();
|
|
if( $this->pay_type == 'cash' ){
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'CASH order. Pay restaurant with your own cash, not PEX.';
|
|
} else {
|
|
return 'CASH order. Pay restaurant with your own cash.';
|
|
}
|
|
} else {
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return 'Pay the restaurant with PEX card';
|
|
} else {
|
|
return 'Pay the restaurant';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function isAtDeliveryRadius(){
|
|
$distance = $this->calcDistance();
|
|
if( $distance ){
|
|
$restaurant = $this->restaurant();
|
|
$delivery_radius = floatval( $restaurant->delivery_radius );
|
|
if( $distance <= $delivery_radius ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function calcDistance(){
|
|
|
|
if( $this->lon && $this->lat ){
|
|
$customerLon = $this->lon;
|
|
$customerLat = $this->lat;
|
|
} else {
|
|
$pos = Crunchbutton_GoogleGeocode::geocode( $this->address );
|
|
if( $pos->lat && $pos->lon ){
|
|
$customerLon = $pos->lon;
|
|
$customerLat = $pos->lat;
|
|
}
|
|
}
|
|
|
|
if( $customerLon && $customerLat ){
|
|
|
|
$restaurant = $this->restaurant();
|
|
$delivery_radius = floatval( $restaurant->delivery_radius );
|
|
|
|
if( $restaurant->delivery_radius_type == 'restaurant' ){
|
|
$restaurantLon = $restaurant->loc_long;
|
|
$restaurantLat = $restaurant->loc_lat;
|
|
} else {
|
|
$community = $restaurant->community()->get( 0 );
|
|
$restaurantLon = $community->loc_lon;
|
|
$restaurantLat = $community->loc_lat;
|
|
}
|
|
|
|
if( $restaurantLat && $restaurantLon ){
|
|
return Crunchbutton_GoogleGeocode::latlonDistanceInMiles( $customerLat, $customerLon, $restaurantLat, $restaurantLon );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function shouldUsePexCard(){
|
|
if( $this->restaurant()->formal_relationship ){
|
|
if( $this->pay_type == 'cash' ){
|
|
$driver = c::user();
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
$driver = c::user();
|
|
if( $this->pay_type == 'cash' ){
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
if( $driver->id_admin && $driver->hasPexCard() ){
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function driverInstructionsFoodStatus(){
|
|
// https://github.com/crunchbutton/crunchbutton/issues/2463#issue-28386594
|
|
// #emergency #6809
|
|
if( ( $this->restaurant()->id_restaurant != 313 && $this->restaurant()->id_restaurant != 316 )
|
|
&& $this->restaurant()->formal_relationship || $this->restaurant()->order_notifications_sent ){
|
|
return 'Food already prepared';
|
|
} else {
|
|
return 'Place the order yourself';
|
|
}
|
|
}
|
|
|
|
// decodes 9 digit order #s
|
|
public static function getByNinjaId($id) {
|
|
$v = $id[0];
|
|
$len = substr($id, -1);
|
|
$id = substr($id, 1, -1);
|
|
$pad = 5;
|
|
|
|
$first = substr($id, 0, 2);
|
|
$rest = substr(strrev(substr($id, 2)),$pad-$len);
|
|
$id = $rest.$first;
|
|
|
|
return $id;
|
|
}
|
|
|
|
// generates 9 digit order #s
|
|
public function ninjaId($version = 1) {
|
|
$id = $this->id;
|
|
if ($this->id >= 10000000) {
|
|
// @todo: ERROR!
|
|
}
|
|
$first = substr($id,-2);
|
|
$rest = substr($id,0,-2);
|
|
$pad = 5; // works until 10 million orders
|
|
|
|
$ret =
|
|
$version
|
|
.$first
|
|
.str_pad(strrev($rest),$pad,'0')
|
|
.strlen($rest);
|
|
|
|
return $ret;
|
|
}
|
|
|
|
// aliases
|
|
public function subtotal(){
|
|
return $this->price;
|
|
}
|
|
|
|
public function __construct($id = null) {
|
|
parent::__construct();
|
|
$this
|
|
->table('order')
|
|
->idVar('id_order')
|
|
->load($id);
|
|
}
|
|
|
|
public function deliveryStatus($type = null) {
|
|
if (!$this->_actions) {
|
|
$this->_actions = Order_Action::q('select * from order_action where id_order=? order by timestamp', [$this->id_order]);
|
|
$this->_deliveryStatus = ['accepted' => false, 'delivered' => false, 'pickedup' => false];
|
|
$acpt = [];
|
|
|
|
foreach ($this->_actions as $action) {
|
|
switch ($action->type) {
|
|
case 'delivery-delivered':
|
|
$this->_deliveryStatus['delivered'] = Admin::o($action->id_admin);
|
|
$this->_deliveryStatus['delivered_date'] = $action->date()->format( 'g:i A' );
|
|
break;
|
|
|
|
case 'delivery-pickedup':
|
|
$this->_deliveryStatus['pickedup'] = Admin::o($action->id_admin);
|
|
$this->_deliveryStatus['pickedup_date'] = $action->date()->format( 'g:i A' );
|
|
break;
|
|
|
|
case 'delivery-accepted':
|
|
$acpt[$action->id_admin] = true;
|
|
$this->_deliveryStatus['accepted_date'] = $action->date()->format( 'g:i A' );
|
|
break;
|
|
|
|
case 'delivery-rejected':
|
|
$acpt[$action->id_admin] = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach ($acpt as $admin => $status) {
|
|
if ($status) {
|
|
$this->_deliveryStatus['accepted'] = Admin::o($admin);
|
|
}
|
|
}
|
|
}
|
|
return $type === null ? $this->_deliveryStatus : $this->_deliveryStatus[$type];
|
|
}
|
|
|
|
public function eta($refresh = false) {
|
|
if (!isset($this->_eta) || $refresh) {
|
|
$eta = Order_Eta::q('select * from order_eta where id_order=? order by date desc limit 1', [$this->id_order])->get(0);
|
|
if (!$eta->id_order_eta || $refresh) {
|
|
$eta = Order_Eta::create($this);
|
|
}
|
|
$this->_eta = $eta;
|
|
}
|
|
return $this->_eta;
|
|
}
|
|
|
|
public function ordersExports( $params = [] ) {
|
|
|
|
$_ignore = [];
|
|
if( isset( $params[ 'ignore' ] ) ){
|
|
foreach( $params[ 'ignore' ] as $key => $val ){
|
|
$_ignore[ $val ] = true;
|
|
}
|
|
}
|
|
|
|
$out = $this->exports( $params );
|
|
$out[ 'delivery_service' ] = intval( $out[ 'delivery_service' ] );
|
|
// $out['user'] = $this->user()->id_user ? $this->user()->exports( [ 'ignore' => [ 'presets', 'points', 'auth', 'tip' ] ] ) : null;
|
|
if( !$_ignore[ 'restaurant' ] ){
|
|
$out['restaurant'] = $this->restaurant()->id_restaurant ? $this->restaurant()->exports() : null;
|
|
}
|
|
|
|
$out['_community_name'] = $this->restaurant()->community()->name;
|
|
$out['_community_permalink'] = $this->restaurant()->community()->permalink;
|
|
$out['_driver_name'] = $this->status()->last()['driver']['name'];
|
|
$out['_driver_phone'] = $this->status()->last()['driver']['phone'];
|
|
$out['_driver_id'] = $this->status()->last()['driver']['id_admin'];
|
|
return $out;
|
|
}
|
|
|
|
public function checkIfOrderWasRefunded( $force = false ){
|
|
if( $this->refunded && !$force ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function ticketsForOutOfDeliveryRadius(){
|
|
|
|
if((intval(Crunchbutton_Config::getVal(Crunchbutton_Order::CONFIG_KEY_GEO_TICKET_NOT_GET_ORDERS)) != 1 )){
|
|
return;
|
|
}
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- 5 min' );
|
|
$orders = Order::q( 'SELECT * FROM `order` WHERE date > ? AND delivery_type = ?', [ $now->format( 'Y-m-d H:i:s' ), self::SHIPPING_DELIVERY ] );
|
|
foreach( $orders as $order ){
|
|
if( !$order->orderHasGeomatchedTicket() ){
|
|
if( !$order->isAtDeliveryRadius() ){
|
|
|
|
if( $order->calcDistance() === false ){
|
|
$pattern = "%s just placed an order, the system could not calculate delivery radius! Order details: Order %d in the %s community to this address %s. Please double check that this address is close enough to be delivered (if it's just slightly out of range it may be fine), and cancel the order if necessary. Thanks!";
|
|
$message = sprintf( $pattern, $order->name, $order->id_order, $order->community()->name, $order->address );
|
|
} else {
|
|
$distance = number_format( $order->calcDistance(), 2 ) . ' miles';
|
|
$pattern = "%s just placed an order out of delivery radius! Distance %s. Order details: Order %d in the %s community to this address %s. Please double check that this address is close enough to be delivered (if it's just slightly out of range it may be fine), and cancel the order if necessary. Thanks!";
|
|
$message = sprintf( $pattern, $order->name, $distance, $order->id_order, $order->community()->name, $order->address );
|
|
}
|
|
|
|
echo $message . "\n";
|
|
Crunchbutton_Support::createNewWarning( [ 'id_order' => $order->id_order, 'body' => $message ] );
|
|
$action = new Crunchbutton_Order_Action;
|
|
$action->id_order = $order->id_order;
|
|
$action->timestamp = date( 'Y-m-d H:i:s' );
|
|
$action->type = Crunchbutton_Order_Action::TICKET_NOT_GEOMATCHED;
|
|
$action->save();
|
|
|
|
if( !$order->id_address ){
|
|
$address = Address::byAddress( $order->address );
|
|
$order->id_address = $address->id_address;
|
|
$order->save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function ticketsForNotGeomatchedOrders(){
|
|
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- 5 min' );
|
|
$orders = Order::q( 'SELECT * FROM `order` WHERE date > ? AND ( geomatched IS NULL OR geomatched = 0 )', [ $now->format( 'Y-m-d H:i:s' ) ] );
|
|
foreach( $orders as $order ){
|
|
if( !$order->orderHasGeomatchedTicket() ){
|
|
$order->createTicketForNotGeomatchedOrder();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function createTicketForNotGeomatchedOrder(){
|
|
// get rid of Place Order Anyway cockpit.la tickets #7904
|
|
return;
|
|
$order = $this;
|
|
if( !$order->geomatched && !$order->orderHasGeomatchedTicket() ){
|
|
$pattern = "%s just did Place Order Anyway! Order details: Order %d in the %s community to this address %s. Please double check that this address is close enough to be delivered (if it's just slightly out of range it may be fine), and cancel the order if necessary. Thanks!";
|
|
$message = sprintf( $pattern, $order->name, $order->id_order, $order->community()->name, $order->address );
|
|
Crunchbutton_Support::createNewWarning( [ 'id_order' => $order->id_order, 'body' => $message ] );
|
|
$action = new Crunchbutton_Order_Action;
|
|
$action->id_order = $order->id_order;
|
|
$action->timestamp = date( 'Y-m-d H:i:s' );
|
|
$action->type = Crunchbutton_Order_Action::TICKET_NOT_GEOMATCHED;
|
|
$action->save();
|
|
|
|
if( !$order->id_address ){
|
|
$address = Address::byAddress( $order->address );
|
|
$order->id_address = $address->id_address;
|
|
$order->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function approve_address(){
|
|
if( $this->id_address ){
|
|
$address = Address::o( $this->id_address );
|
|
$address->status = Crunchbutton_Address::STATUS_APPROVED;
|
|
$address->save();
|
|
return $address;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static function ticketForCampusCashOrder(){
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- 5 min' );
|
|
$orders = Order::q( 'SELECT * FROM `order` WHERE date > ? AND campus_cash = 1', [ $now->format( 'Y-m-d H:i:s' ) ] );
|
|
$pattern = "Charge this customer now on Verifone in front room and mark as Already Charged from this Support ticket! - More info: %s just placed an %s (Campus Cash) Order! Order details: Order %s in the %s community to this address %s";
|
|
foreach( $orders as $order ){
|
|
if( !$order->orderHasCampusCashTicket() ){
|
|
$campus_cash_name = $order->campusCashName();
|
|
$message = sprintf( $pattern, $order->name, $campus_cash_name, $order->id_order, $order->community()->name, $order->address );
|
|
echo $message . "\n";
|
|
Crunchbutton_Support::createNewWarning( [ 'id_order' => $order->id_order, 'body' => $message, 'bubble' => true ] );
|
|
$action = new Crunchbutton_Order_Action;
|
|
$action->id_order = $order->id_order;
|
|
$action->timestamp = date( 'Y-m-d H:i:s' );
|
|
$action->type = Crunchbutton_Order_Action::TICKET_CAMPUS_CASH;
|
|
$action->save();
|
|
}
|
|
}
|
|
self::ticketToReminderToChargeCampusCashOrder();
|
|
}
|
|
|
|
public static function ticketToReminderToChargeCampusCashOrder(){
|
|
$now = new DateTime( 'now', new DateTimeZone( c::config()->timezone ) );
|
|
$now->modify( '- 15 min' );
|
|
$orders = Order::q( 'SELECT * FROM `order` WHERE date <= ? AND campus_cash = 1 AND id_order NOT IN ( SELECT id_order FROM order_transaction WHERE type = ? )', [ $now->format( 'Y-m-d H:i:s' ), Crunchbutton_Order_Transaction::TYPE_CAMPUS_CASH_CHARGED ] );
|
|
$pattern = "Remind to charge this customer now on Verifone in front room and mark as Already Charged from this Support ticket!";
|
|
foreach( $orders as $order ){
|
|
if( !$order->orderHasCampusCashTicketReminder() && !$order->campus_cash_charged() ){
|
|
$campus_cash_name = $order->campusCashName();
|
|
$message = sprintf( $pattern, $order->name, $campus_cash_name, $order->id_order, $order->community()->name, $order->address );
|
|
echo $message . "\n";
|
|
Crunchbutton_Support::createNewWarning( [ 'id_order' => $order->id_order, 'body' => $message, 'bubble' => true ] );
|
|
$action = new Crunchbutton_Order_Action;
|
|
$action->id_order = $order->id_order;
|
|
$action->timestamp = date( 'Y-m-d H:i:s' );
|
|
$action->type = Crunchbutton_Order_Action::TICKET_CAMPUS_CASH_REMINDER;
|
|
$action->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function orderHasGeomatchedTicket(){
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND type = ? ORDER BY id_order_action DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Action::TICKET_NOT_GEOMATCHED ] );
|
|
if( $action->id_order_action ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function orderHasCampusCashTicket(){
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND type = ? ORDER BY id_order_action DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Action::TICKET_CAMPUS_CASH ] );
|
|
if( $action->id_order_action ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function orderHasCampusCashTicketReminder(){
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND type = ? ORDER BY id_order_action DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Action::TICKET_CAMPUS_CASH_REMINDER ] );
|
|
if( $action->id_order_action ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function orderHasRepsHasFailedTicket(){
|
|
$action = Crunchbutton_Order_Action::q( 'SELECT * FROM order_action WHERE id_order = ? AND type = ? ORDER BY id_order_action DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Action::TICKET_REPS_FAILED_PICKUP ] );
|
|
if( $action->id_order_action ){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function notifyDriverAboutCustomerChanges( $changes ){
|
|
|
|
if( count( $changes ) ){
|
|
|
|
$message = 'The order #' . $this->id_order . ' was updated:';
|
|
if( $changes[ 'name' ] ){
|
|
$message .= "\nName was changed to: " . $this->name;
|
|
}
|
|
if( $changes[ 'phone' ] ){
|
|
$message .= "\nPhone was changed to: " . $this->phone;
|
|
}
|
|
if( $changes[ 'address' ] ){
|
|
$message .= "\nAddress was changed to: " . $this->address;
|
|
}
|
|
|
|
$status = $this->status()->last();
|
|
|
|
if( $status[ 'status' ] == 'delivered' ){
|
|
return;
|
|
}
|
|
|
|
if( $status && $status[ 'driver' ] ){
|
|
|
|
$driver = Admin::o( $status[ 'driver' ] );
|
|
|
|
$notifications = $driver->getNotifications();
|
|
|
|
$message = $driver->firstName() .': ' . $message;
|
|
|
|
foreach( $notifications as $notification ){
|
|
|
|
if( $notification->active ){
|
|
switch ( $notification->type ) {
|
|
case Crunchbutton_Admin_Notification::TYPE_SMS:
|
|
case Crunchbutton_Admin_Notification::TYPE_PHONE:
|
|
case Crunchbutton_Admin_Notification::TYPE_DUMB_SMS:
|
|
$notification->sendSms( $this, $message );
|
|
break;
|
|
|
|
case Crunchbutton_Admin_Notification::TYPE_PUSH_IOS:
|
|
// $notification->sendPushIos( $this, $message );
|
|
break;
|
|
|
|
case Crunchbutton_Admin_Notification::TYPE_PUSH_ANDROID:
|
|
// $notification->sendPushAndroid( $this, $message );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
public function campus_cash_charged(){
|
|
$transaction = Crunchbutton_Order_Transaction::q( 'SELECT * FROM order_transaction WHERE id_order = ? AND type = ? ORDER BY id_order_transaction DESC LIMIT 1', [ $this->id_order, Crunchbutton_Order_Transaction::TYPE_CAMPUS_CASH_CHARGED ] )->get( 0 );
|
|
if( $transaction->id_order_transaction ){
|
|
return [ 'name' => $transaction->admin()->name, 'date' => $transaction->date()->format( 'M dS g:i a' ) ];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static function orderLoadingPhrases(){
|
|
$out = [];
|
|
$phrases = Config::q('SELECT * FROM config WHERE `key` = ?', [self::ORDER_LOADING_PHRASE_KEY]);
|
|
foreach($phrases as $phrase){
|
|
$out[] = $phrase->value;
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
public function hasDriverToDeliveryPreOrder(){
|
|
if($this->preordered && $this->date_delivery){
|
|
$community = $this->restaurant()->community();
|
|
if(Community_Shift::hasAssignedShiftAtDate($community->id_community, $this->date_delivery)){
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static function pickOrderLoadingPhrase(){
|
|
$phrases = self::orderLoadingPhrases();
|
|
if(count($phrases)){
|
|
return $phrases[ array_rand( $phrases ) ];
|
|
}
|
|
}
|
|
|
|
public function save($new = false) {
|
|
|
|
$new = $this->id_order ? false : true;
|
|
|
|
$phone = Phone::byPhone( $this->phone );
|
|
$this->id_phone = $phone->id_phone;
|
|
|
|
parent::save();
|
|
|
|
Event::create([
|
|
'room' => [
|
|
'order.'.$this->id_order,
|
|
'orders',
|
|
'restaurant.'.$this->id_restaurant.'.orders',
|
|
'user.'.$this->id_user.'.orders'
|
|
]
|
|
], $new ? 'create' : 'update', $this->ordersExports());
|
|
}
|
|
}
|