2014-06-02 11:17:00 -03:00

2362 lines
74 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 Cana_Table {
const PAY_TYPE_CASH = 'cash';
const PAY_TYPE_CREDIT_CARD = 'card';
const SHIPPING_DELIVERY = 'delivery';
const SHIPPING_TAKEOUT = 'takeout';
const TIP_PERCENT = 'percent';
const TIP_NUMBER = 'number';
/**
* 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)
{
$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'];
// 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'],
'cart' => $params['cart'],
'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,
]);
}
$this->id_restaurant = $params['restaurant'];
// Check if the restaurant is active #2938
if( $this->restaurant()->active == 0 ){
$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 == 0 && $this->restaurant()->takeout == 1 ){
$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 == 0 && $this->restaurant()->delivery == 1 ){
$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;
foreach ($params['cart'] as $d) {
$dish = new Order_Dish;
$dish->id_dish = $d['id'];
$price = $dish->dish()->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 = number_format( $price_delivery_markup, 2 );
}
$subtotal += $price;
$subtotal_plus_delivery_service_markup += $price_delivery_markup;
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 = number_format( $price_delivery_markup, 2 );
}
$subtotal_plus_delivery_service_markup += $price_delivery_markup;
$subtotal += $price;
// $subtotal += $option->option()->optionPrice($d['options']);
$dish->_options[] = $option;
}
}
$this->_dishes[] = $dish;
}
// 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);
$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( intval( $this->restaurant()->delivery_service ) == 1 ){
$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 );
if( intval( $this->restaurant()->delivery_service ) == 1 ){
$this->final_price = Util::ceil( $totalWithFees + $tax, 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, 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;
}
}
if (!$this->restaurant()->open()) {
$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.'.';
}
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 = User::o($user->id_user);
$this->id_user = $user->id_user;
// 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 ) != '' ){
$giftCardAdded = false;
$giftcards = Crunchbutton_Promo::validateNotesField( $this->notes, $this->id_restaurant );
foreach ( $giftcards[ 'giftcards' ] as $giftcard ) {
if( $giftcard->id_promo ){
if( !$giftCardAdded ){
$this->giftcardValue += $giftcard->value;
$giftCardAdded = true;
}
}
}
$_notes = $giftcards[ 'notes' ];
}
Log::debug([
'issue' => '#1551',
'method' => 'process',
'$this->final_price' => $this->final_price,
'giftcardValue'=> $this->giftcardValue,
'$_notes' => $_notes,
'$this->notes' => $this->notes
]);
// process the payment
$res = $this->verifyPayment();
// failed to process the card
if ($res !== true) {
Log::debug([
'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'],
'cart' => $params['cart'],
'type' => 'order-log'
]);
return $res['errors'];
// successfully processed the card
} else {
$this->txn = $this->transaction();
}
$user->location_lat = $params['lat'];
$user->location_lon = $params['lon'];
$user->name = $this->name;
$user->phone = $this->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( $user->id_user );
$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']) {
// 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->_customer->id;
break;
case 'balanced':
default:
$payment_type->balanced_id = $this->_paymentType->id;
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 );
if ($this->_customer->id) {
switch (Crunchbutton_User_Payment_Type::processor()) {
case 'balanced':
$this->_customer->email_address = 'uuid-'.$user->uuid.'@_DOMAIN_';
try {
$this->_customer->save();
} catch (Exception $e) {
}
break;
}
}
c::auth()->session()->id_user = $user->id_user;
c::auth()->session()->generateAndSaveToken();
$agent = Crunchbutton_Agent::getAgent();
$this->id_agent = $agent->id_agent;
if( c::auth()->session()->id_session != '' ){
$this->id_session = c::auth()->session()->id_session;
}
$this->id_user = $this->_user->id_user;
$this->date = date('Y-m-d H:i:s');
$this->delivery_service = $this->restaurant()->hasDeliveryService();
$this->id_community = $this->restaurant()->community()->id_community;
$this->save();
// If the payment succeds then redeem the gift card
if ( trim( $this->notes ) != '' ){
$giftcards = Crunchbutton_Promo::validateNotesField( $this->notes, $this->id_restaurant );
$giftCardAdded = false;
foreach ( $giftcards[ 'giftcards' ] as $giftcard ) {
if( $giftcard->id_promo ){
if( !$giftCardAdded ){
$giftcard->addCredit( $user->id_user );
}
$giftCardAdded = true;
}
}
$this->notes = $giftcards[ 'notes' ];
}
Log::debug([ 'issue' => '#1551', 'method' => 'process', '$this->final_price' => $this->final_price, 'giftcardValue'=> $this->giftcardValue, '$this->notes' => $this->notes ]);
$this->debitFromUserCredit( $user->id_user );
if ( $params['make_default'] == 'true' ) {
$preset = $user->preset($this->restaurant()->id_restaurant);
if ($preset->id_preset) {
$preset->delete();
}
}
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();
}
}
}
$this->que();
$order = $this;
if ( $params['make_default'] == 'true' ) {
// Cana::timeout(function() use($order) {
Preset::cloneFromOrder($order);
// });
}
Cana::timeout(function() use($order) {
Crunchbutton_Hipchat_Notification::OrderPlaced($order);
});
Cana::timeout(function() use($order) {
$rules = new Crunchbutton_Order_Rules();
$rules->run( $order );
});
if( Crunchbutton_Referral::isReferralEnable() ){
// If the user was invited we'll give credit to the inviter user
$inviter_code = Crunchbutton_Referral::checkCookie();
if( $inviter_code ){
// If the code is valid it will return the inviter user
$_inviter = Crunchbutton_Referral::validCode( $inviter_code );
if( $_inviter ){
$totalOrdersByPhone = $this->totalOrdersByPhone( $this->phone );
$referral = new Crunchbutton_Referral();
$referral->id_user_inviter = $_inviter->id_user;
$referral->id_user_invited = $this->id_user;
$referral->id_order = $this->id_order;
$referral->invite_code = $inviter_code;
if( $totalOrdersByPhone <= 1 ){
$referral->new_user = 1;
} else {
$referral->new_user = 0;
}
$referral->date = date('Y-m-d H:i:s');
$referral->save();
// See #1660
if( $this->pay_type == 'card' ){
// Finally give credit to inviter
$referral->addCreditToInviter();
}
Log::debug([ 'inviter_code' => $inviter_code, 'totalOrdersByPhone' => $totalOrdersByPhone, 'type' => 'referral', 'pay_type' => $this->pay_type ]);
}
Crunchbutton_Referral::removeCookie();
}
}
return true;
}
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 $totalCredit;
}
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() {
return Restaurant::o($this->id_restaurant);
}
public function user() {
return User::o($this->id_user);
}
public function accepted() {
$nl = Notification_Log::q('select * from notification_log where id_order="'.$this->id_order.'" and status="accepted"');
return $nl->count() ? true : false;
}
public function fax_succeeds() {
$nl = Notification_Log::q('select * from notification_log where id_order="'.$this->id_order.'" and type="phaxio" and status="success"');
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() {
if (!isset($this->_date)) {
$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':
$user = c::user()->id_user ? c::user() : null;
if( $user ){
$paymentType = $user->payment_type();
}
if (!$this->_card['id'] && !$paymentType->id_user_payment_type && $user->balanced_id) {
// user only has a balanced customer id, not a payment. copy payment type over
$paymentType = (new User_Payment_Type([
'id_user' => $user->id_user,
'active' => 1,
// 'stripe_id' => $user->stripe_id,
'balanced_id' => $user->balanced_id,
'card' => $user->card,
'card_exp_month' => $user->card_exp_month,
'card_exp_year' => $user->card_exp_year,
'date' => date('Y-m-d H:i:s')
]))->save();
}
if (!$this->_card['id'] && $paymentType->id_user_payment_type) {
// use a stored users card and the apporiate payment type
if ($paymentType->balanced_id) {
if (substr($paymentType->balanced_id,0,2) != 'CC') {
// we have stored the customer and not the payment type. need to fix that
$cards = Crunchbutton_Balanced_Account::byId($paymentType->balanced_id)->cards;
if (get_class($cards) == 'RESTful\Collection') {
foreach ($cards as $card) {
$c = $card;
}
$paymentType = (new User_Payment_Type([
'id_user' => $user->id_user,
'active' => 1,
'balanced_id' => $c->id,
'card' => str_replace('x','*',$c->number),
'card_exp_month' => $c->expiration_year,
'card_exp_year' => $c->expiration_month,
'date' => date('Y-m-d H:i:s')
]))->save();
}
}
$charge = new Charge_Balanced([
'card_id' => $paymentType->balanced_id
]);
} elseif ($paymentType->stripe_id) {
$charge = new Charge_Stripe([
'stripe_id' => $paymentType->stripe_id
]);
}
}
if (!$charge) {
switch (Crunchbutton_User_Payment_Type::processor()) {
case 'balanced':
$charge = new Charge_Balanced();
break;
case 'stripe':
$charge = new Charge_Stripe([
'stripe_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,
'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 ){
$interval = $hours . ' HOUR';
$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 ';
}
$where .= ' AND o.delivery_service = 1 ';
$where .= ' AND date > DATE_SUB( NOW(), INTERVAL ' . $interval . ' )';
$query = 'SELECT DISTINCT( o.id_order ) id, o.* FROM `order` o ' . $where . ' ORDER BY o.id_order';
return Order::q( $query );
}
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 = 1
' . $innerJoin . $where . '
ORDER BY o.id_order DESC ' . $limit;
return Order::q( $query );
}
public static function find($search = []) {
$query = '
select `order`.* from `order`
left join restaurant using(id_restaurant)
where id_order is not null
';
if ($search['env']) {
$query .= ' and env="'.$search['env'].'" ';
}
if ($search['processor']) {
$query .= ' and processor="'.$search['processor'].'" ';
}
if ($search['start']) {
$s = new DateTime($search['start']);
$query .= ' and DATE(`date`)>="'.$s->format('Y-m-d').'" ';
}
if ($search['end']) {
$s = new DateTime($search['end']);
$query .= ' and DATE(`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 `order`.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 `order`.id_restaurant IN ( ' . join( $restaurants, ',' ) . ')';
}
}
if ($search['community']) {
$query .= ' and `restaurant`.community="'.$search['community'].'" ';
}
if ($search['order']) {
$query .= ' and `order`.id_order="'.$search['order'].'" ';
}
if ($search['search']) {
$qn = '';
$q = '';
$searches = explode(' ',$search['search']);
foreach ($searches as $word) {
if ($word{0} == '-') {
$qn .= ' and `order`.name not like "%'.substr($word,1).'%" ';
$qn .= ' and `order`.address not like "%'.substr($word,1).'%" ';
$qn .= ' and `order`.phone not like "%'.substr($word,1).'%" ';
$qn .= ' and `restaurant`.name not like "%'.substr($word,1).'%" ';
} else {
$q .= '
and (`order`.name like "%'.$word.'%"
or `order`.id_order = "'.$word.'"
or `order`.address like "%'.$word.'%"
or `restaurant`.name like "%'.$word.'%"
or REPLACE( `order`.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() {
if (!isset($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( intval( $this->delivery_service ) == 1 ){
$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() {
return number_format(($this->price + $this->delivery_fee) * ($this->service_fee/100),2);
}
public function cbFee() {
return ($this->restaurant()->fee_restaurant) * ($this->price) / 100;
}
public function customer_fee(){
return ($this->restaurant()->fee_customer) * ($this->price) / 100;
}
public function fee(){
if( $this->restaurant()->fee_on_subtotal ){
return $this->cbFee();
} else {
return ($this->restaurant()->fee_restaurant) * ($this->final_price) / 100;
}
}
public function notify(){
$order = $this;
foreach ( $order->restaurant()->notifications() as $n ) {
// Admin notification type means it needs a driver
if( $n->type != Crunchbutton_Notification::TYPE_ADMIN ){
Log::debug([ 'order' => $order->id_order, 'action' => 'sending notification', 'type' => $n->type, 'to' => $n->value, 'type' => 'notification']);
$n->send( $order );
}
}
if( intval( $order->restaurant()->delivery_service ) == 1 ){
$this->notifyDrivers();
}
}
public function notifyDrivers(){
if( $this->ignoreDrivers ){
return;
}
$order = $this;
$needDrivers = false;
$hasDriversWorking = false;
$driversToNotify = [];
foreach ( $order->restaurant()->notifications() as $n ) {
// Admin notification type means it needs a driver
if( $n->type == Crunchbutton_Notification::TYPE_ADMIN ){
$needDrivers = true;
$admin = $n->admin();
// Store the drivers
$driversToNotify[ $admin->id_admin ] = $admin;
}
}
// check if the restaurant is using our delivery system
if( intval( $order->restaurant()->delivery_service ) == 1 ){
$needDrivers = true;
// get the restaurant community and its drivers
$communities = $order->restaurant()->communities();
foreach( $communities as $community ){
if( $community->id_community ){
$drivers = $community->getDriversOfCommunity();
foreach( $drivers as $driver ){
$driversToNotify[ $driver->id_admin ] = $driver;
}
}
}
// legacy - lets keep it here for while
$community = $order->restaurant()->community;
if( $community ){
$group = Crunchbutton_Group::getDeliveryGroupByCommunity( Crunchbutton_Group::driverGroupOfCommunity( $community ) );
if( $group->id_group ){
$drivers = Crunchbutton_Admin::q( "SELECT a.* FROM admin a INNER JOIN admin_group ag ON ag.id_admin = a.id_admin AND ag.id_group = {$group->id_group}" );
foreach( $drivers as $driver ){
$driversToNotify[ $driver->id_admin ] = $driver;
}
}
}
}
$driverAlreadyNotified = [];
$drivers = Crunchbutton_Community_Shift::driversCouldDeliveryOrder( $order->id_order );
if( $drivers ){
foreach( $drivers as $driver ){
$driverAlreadyNotified[] = $driver->id_admin;
foreach( $driver->activeNotifications() as $adminNotification ){
$adminNotification->send( $order );
$hasDriversWorking = true;
$message = '#'.$order->id_order.' sending ** NEW ** notification to ' . $driver->name . ' # ' . $adminNotification->value;
Log::debug( [ 'order' => $order->id_order, 'action' => $message, 'type' => 'delivery-driver' ] );
}
}
}
// Send notification to drivers
if( count( $driversToNotify ) > 0 ){
foreach( $driversToNotify as $driver ){
if( $driver->isWorking() ){
if( in_array( $driver->id_admin, $driverAlreadyNotified ) ){
$message = '#'.$order->id_order.' notification to ' . $driver->name . ' already sent';
Log::debug( [ 'order' => $order->id_order, 'action' => $message, 'type' => 'delivery-driver' ] );
continue;
}
foreach( $driver->activeNotifications() as $adminNotification ){
$hasDriversWorking = true;
$adminNotification->send( $order );
Log::debug([ 'order' => $order->id_order, 'action' => 'sending notification', 'type' => 'admin', 'type' => 'notification']);
}
}
}
Crunchbutton_Admin_Notification_Log::register( $this->id_order );
}
if( $needDrivers && !$hasDriversWorking ){
Log::debug([ 'order' => $order->id_order, 'action' => 'there is no drivers to get the order', 'type' => 'notification']);
Crunchbutton_Admin_Notification::warningAboutNoRepsWorking( $order );
}
}
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 = {$this->id_order} AND type != 'delivery-rejected' ORDER BY id_order_action DESC LIMIT 1" );
}
return $this->_driver;
}
public function resend_notify_drivers(){
$order = $this;
Crunchbutton_Admin_Notification_Log::cleanLog( $order->id_order );
$order->notifyDrivers();
}
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();
}
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="'.$this->id_order.'" AND type = "confirm" AND ( status = "created" OR status = "queued" OR status ="success" ) ');
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 = 'http://'.c::config()->host_callback.'/api/order/'.$this->id_order.'/doconfirmstealth';
} else {
$confirmURL = 'http://'.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 = new Services_Twilio( c::config()->twilio->{$env}->sid, c::config()->twilio->{$env}->token );
$call = $twilio->account->calls->create(
c::config()->twilio->{$env}->outgoingRestaurant,
'+1'.$num,
$confirmURL,
[
'StatusCallback' => 'http://'.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() {
$env = c::getEnv();
$num = ($env == 'live' ? $this->phone : c::config()->twilio->testnumber);
$twilio = new Twilio(c::config()->twilio->{$env}->sid, c::config()->twilio->{$env}->token);
$message = str_split($this->message('selfsms'),160);
$type = 'twilio';
Log::debug( [ 'order' => $this->id_order, 'action' => 'receipt', 'num' => $num, 'message' => $message, 'type' => 'notification' ]);
foreach ($message as $msg) {
switch ($type) {
case 'googlevoice':
$gv = new GoogleVoice('cbvoice@arzynik.com', base64_decode('eXVtbWllc3Q='));
$gv->sms($num, $msg);
break;
case 'twilio':
$twilio->account->sms_messages->create(
c::config()->twilio->{$env}->outgoingTextCustomer,
'+1'.$num,
$msg
);
break;
case 'textbelt':
default:
$cmd = 'curl http://crunchr.co:9090/text \\'
.'-d number='.$num.' \\'
.'-d "message='.$msg.'"';
exec($cmd, $return);
print_r($return);
break;
}
}
}
public function que( $sendReceipt = true ) {
$order = $this;
Cana::timeout(function() use($order) {
/* @var $order Crunchbutton_Order */
$order->notify();
});
if( $sendReceipt ){
c::timeout(function() use($order) {
$order->receipt();
}, 30 * 1000); // 30 seconds
} else {
Log::debug( [ 'order' => $order->id_order, 'action' => 'receipt already sent', 'type' => 'notification' ]);
}
// Start the timer to check if the order was confirmed. #1049
// if ($this->restaurant()->confirmation) {
// $timer = c::config()->twilio->warningOrderNotConfirmedTime;
// Log
// Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningOrderNotConfirmed started', 'time' => $timer, 'type' => 'notification' ]);
/* Removed for while by @pererinha asked by @DavidKlumpp at 11/07/2013
c::timeout(function() use($order, $timer) {
$order->warningOrderNotConfirmed();
}, $timer );
*/
// }
}
// After 5 minutes the fax was sent we have to send this confirmation to make sure that the fax as delivered. If the order was already confirmed this confirmation will be ignored.
public function queConfirmFaxWasReceived(){
// Issue #1239
return false;
$order = $this;
$isConfirmed = Order::isConfirmed( $order->id_order );
if ( $isConfirmed || !$order->restaurant()->confirmation) {
return;
}
$confirmTimeFaxReceived = c::config()->twilio->confirmTimeFaxReceived;
// Log
Log::debug( [ 'order' => $this->id_order, 'action' => 'confirmFaxWasReceived', 'confirmationTime' => $confirmTimeFaxReceived, 'confirmed' => $isConfirmed, 'type' => 'notification' ] );
c::timeout(function() use($order) {
$order->confirm();
}, $confirmTimeFaxReceived );
}
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="'.$order->id_order.'" AND type = "confirm" AND ( status = "created" OR status = "queued" ) ');
if( $nl->count() > 0 ){
return;
}
// Query to count the number of confirmations sent
$nl = Notification_Log::q('SELECT * FROM notification_log WHERE id_order="'.$order->id_order.'" AND status="callback" AND `type`="confirm"');
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' ] );
// $order = $this;
Cana::timeout(function() use($order) {
/* @var $order Crunchbutton_Order */
$order->confirm();
}, $confirmationTime );
}
// 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}";
$message = str_split( $message,160 );
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningStealthNotConfirmed sending sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
$twilio = new Twilio( c::config()->twilio->{$env}->sid, c::config()->twilio->{$env}->token );
// keep this ugly true for tests only
if( $env == 'live' ){
foreach ( Crunchbutton_Support::getUsers() as $supportName => $supportPhone ) {
foreach ( $message as $msg ) {
Log::debug( [ 'order' => $order->id_order, 'action' => 'warningStealthNotConfirmed', 'message' => $message, 'supportName' => $supportName, 'supportPhone' => $supportPhone, 'type' => 'notification' ]);
try {
$twilio->account->sms_messages->create(
c::config()->twilio->{$env}->outgoingTextCustomer,
'+1'.$supportPhone,
$msg
);
} catch (Exception $e) {}
}
}
} 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;
$message = str_split( $message,160 );
Log::debug( [ 'order' => $order->id_order, 'action' => 'que warningOrderNotConfirmed sending sms', 'confirmed' => $isConfirmed, 'type' => 'notification' ]);
$twilio = new Twilio( c::config()->twilio->{$env}->sid, c::config()->twilio->{$env}->token );
if( $env == 'live' ){
foreach ( Crunchbutton_Support::getUsers() as $supportName => $supportPhone ) {
foreach ( $message as $msg ) {
Log::debug( [ 'order' => $order->id_order, 'action' => 'warningOrderNotConfirmed', 'message' => $message, 'supportName' => $supportName, 'supportPhone' => $supportPhone, 'type' => 'notification' ]);
try {
$twilio->account->sms_messages->create(
c::config()->twilio->{$env}->outgoingTextCustomer,
'+1'.$supportPhone,
$msg
);
} catch (Exception $e) {}
}
}
} 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";
}
// Start Telling Customers Estimated Delivery Time #2476
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";
}
$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 'sms-driver':
$spacer = ' / ';
$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;
}
$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 ){
$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 ){
$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':
$spacer = ' / ';
$payment =
$msg = $this->name . $spacer . strtoupper( $this->pay_type ) . $spacer . strtoupper( $this->delivery_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 ;
// Payment is card and user tipped
if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && $this->tip ){
$msg .= 'TIP ' . $this->tip();
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CREDIT_CARD && !$this->tip ){
$msg .= 'TIP BY CASH';
} else if( $this->pay_type == Crunchbutton_Order::PAY_TYPE_CASH ){
$msg .= 'TOTAL ' . $this->final_price;
}
$msg .= $spacer . $this->driverInstructionsFoodStatus() . $spacer . $this->driverInstructionsPaymentStatus();
break;
}
return $msg;
}
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() {
$out = $this->properties();
unset($out['id_user']);
unset($out['id']);
unset($out['id_order']);
unset($out['delivery_service_markup']);
unset($out['delivery_service_markup_value']);
$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['type'] );
unset( $out['uuid'] );
unset( $out['restaurant_name'] );
unset( $out['restaurant_permalink'] );
} else {
$date = new DateTime( $this->date, new DateTimeZone( $this->restaurant()->timezone ) );
$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;
$out['_restaurant_delivery_estimated_time_formated'] = $this->restaurant()->calc_delivery_estimated_time( $this->date );
$out['_restaurant_pickup_estimated_time_formated'] = $this->restaurant()->calc_pickup_estimated_time( $this->date );
$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;
}
$date = new DateTime($this->date);
$date->setTimeZone($timezone);
$out['_date_tz'] = $date->format('Y-m-d H:i:s');
$out['_tz'] = $date->format('T');
$out['summary'] = $this->orderMessage('summary');
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->note = 'Value ' . $credit->value . ' refunded from order: ' . $this->id_order . ' - ' . date('Y-m-d H:i:s');
$creditRefounded->save();
}
}
}
}
public function refund() {
if (!$this->refunded){
// Refund the gift
$this->refundGiftFromOrder();
if ( intval( $this->charged() ) > 0 ) {
if ($this->pay_type == self::PAY_TYPE_CREDIT_CARD) {
switch ($this->processor) {
case 'stripe':
default:
$env = c::getEnv();
Stripe::setApiKey(c::config()->stripe->{$env}->secret);
$ch = Stripe_Charge::retrieve($this->txn);
try {
$ch->refund();
} catch (Exception $e) {
return false;
}
break;
case 'balanced':
try {
// refund the debit
$ch = Crunchbutton_Balanced_Debit::byId($this->txn);
$ch->refund();
} catch (Exception $e) {
print_r($e);
return false;
}
$res = false;
try {
// cancel the hold
$hold = Crunchbutton_Balanced_CardHold::byOrder($this);
$res = $hold->void();
} catch (Exception $e) {
}
if (!$res) {
Log::debug([
'order' => $this->id_order,
'action' => 'refund',
'status' => 'failed to void hold'
]);
}
break;
}
}
}
$support = $this->getSupport();
if ($support) {
$support->addSystemMessage('Order refunded.');
}
$this->refunded = 1;
$this->save();
return true;
}
return false;
}
public function getSupport() {
$support = Support::getSupportForOrder($this->id_order);
return $support;
}
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 function lastTippedOrder( $id_user = null ) {
$id_user = ( $id_user ) ? $id_user : $this->id_user;
return self::q('select * from `order` where id_user="'.$id_user.'" and tip is not null and tip > 0 order by id_order desc limit 0,1');
}
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="'.$id_user.'" and delivery_type = "' . $delivery . '" and tip is not null order by id_order desc limit 0,1');
if( $order->tip ){
return $order->tip;
}
}
return null;
}
public 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 = ' . $id_user . ' AND delivery_type = "delivery" ORDER BY id_order DESC LIMIT 1');
if( $order->id_order ){
return Order::o( $order->id_order );
}
}
return null;
}
public function lastTip( $id_user = null ) {
$last_order = self::lastTippedOrder( $id_user );
if( $last_order->tip ){
return $last_order->tip;
}
return null;
}
public 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 community() {
return Community::o($this->id_community);
}
public function hasGiftCard(){
if( !$this->id_order ){
return 0;
}
$query = 'SELECT SUM( value ) as total FROM promo WHERE id_order_reference = ' . $this->id_order;
$row = Cana::db()->get( $query )->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 = ' . $this->id_order . ' AND id_promo IS NULL';
$row = Cana::db()->get( $query )->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() {
$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 function totalOrdersByPhone( $phone ){
$query = "SELECT COUNT(*) AS total FROM `order` WHERE phone = '{$phone}'";
$row = Cana::db()->get( $query )->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 );
}
/*
get the delivery status of the order based on reps or restaurants actions agaisnt it
@todo: add restaurant actions
*/
public function deliveryStatus($type = null) {
if (!$this->_actions) {
$this->_actions = Order_Action::q('select * from order_action where id_order="'.$this->id_order.'" order by timestamp');
$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);
break;
case 'delivery-pickedup':
$this->_deliveryStatus['pickedup'] = Admin::o($action->id_admin);
break;
case 'delivery-accepted':
$acpt[$action->id_admin] = true;
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 deliveryAccept($admin) {
if ($this->deliveryStatus('accepted')) {
return false;
}
(new Order_Action([
'id_order' => $this->id_order,
'id_admin' => $admin->id_admin,
'timestamp' => date('Y-m-d H:i:s'),
'type' => 'delivery-accepted'
]))->save();
$this->_actions = null;
return true;
}
public function deliveryReject($admin) {
(new Order_Action([
'id_order' => $this->id_order,
'id_admin' => $admin->id_admin,
'timestamp' => date('Y-m-d H:i:s'),
'type' => 'delivery-rejected'
]))->save();
$this->_actions = null;
return true;
}
public function deliveryPickedup($admin) {
if (!$this->deliveryStatus('accepted') || $this->deliveryStatus('accepted')->id_admin != $admin->id_admin) {
return false;
}
(new Order_Action([
'id_order' => $this->id_order,
'id_admin' => $admin->id_admin,
'timestamp' => date('Y-m-d H:i:s'),
'type' => 'delivery-pickedup'
]))->save();
$this->_actions = null;
return true;
}
public function deliveryDelivered($admin) {
if (!$this->deliveryStatus('accepted') || $this->deliveryStatus('accepted')->id_admin != $admin->id_admin) {
return false;
}
(new Order_Action([
'id_order' => $this->id_order,
'id_admin' => $admin->id_admin,
'timestamp' => date('Y-m-d H:i:s'),
'type' => 'delivery-delivered'
]))->save();
$this->_actions = null;
return true;
}
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;
}
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 );
}
if( $statuses[ 'pickedup' ] ){
return array( 'status' => 'pickedup', 'name' => $statuses[ 'pickedup' ]->name, 'id_admin' => $statuses[ 'pickedup' ]->id_admin, 'order' => 2 );
}
if( $statuses[ 'accepted' ] ){
return array( 'status' => 'accepted', 'name' => $statuses[ 'accepted' ]->name, 'id_admin' => $statuses[ 'accepted' ]->id_admin, 'order' => 1 );
}
return array ( 'status' => 'new', 'order' => 0 );
}
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_DELIVERED . "' )";
$action = Crunchbutton_Order_Action::q( $query );
if( $action->count() > 0 ){
return true;
}
return false;
}
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 = {$this->id_order}" );
if( $credit->count() > 0 ){
return true;
}
return false;
}
public function getDeliveryDriver(){
$action = Crunchbutton_Order_Action::q( "SELECT * FROM order_action WHERE id_order = {$this->id_order} AND ( type = '" . Crunchbutton_Order_Action::DELIVERY_PICKEDUP . "' OR type = '" . Crunchbutton_Order_Action::DELIVERY_ACCEPTED . "' OR type = '" . Crunchbutton_Order_Action::DELIVERY_DELIVERED . "') LIMIT 1" );
if( $action->id_admin ){
return $action->admin();
}
}
public function deliveryExports() {
return [
'id_order' => $this->id_order,
'uuid' => $this->uuid,
'delivery-status' => [
'delivered' => $this->deliveryStatus('delivered') ? $this->deliveryStatus('delivered')->publicExports() : false,
'pickedup' => $this->deliveryStatus('pickedup') ? $this->deliveryStatus('pickedup')->publicExports() : false,
'accepted' => $this->deliveryStatus('accepted') ? $this->deliveryStatus('accepted')->publicExports() : false
],
'self-reply' => $this->deliveryReply(c::admin())
];
}
public function driverInstructionsPaymentStatus(){
// https://github.com/crunchbutton/crunchbutton/issues/2463#issue-28386594
if( $this->restaurant()->formal_relationship == 1 ){
if( $this->pay_type == 'cash' ){
return 'Pay the restaurant';
} else {
return 'Do not pay the restaurant';
}
} else {
return 'Pay the restaurant';
}
}
public function driverInstructionsFoodStatus(){
// https://github.com/crunchbutton/crunchbutton/issues/2463#issue-28386594
if( $this->restaurant()->formal_relationship == 1 || $this->restaurant()->order_notifications_sent == 1 ){
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;
}
public function __construct($id = null) {
parent::__construct();
$this
->table('order')
->idVar('id_order')
->load($id);
}
}