1008 lines
28 KiB
PHP
1008 lines
28 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'];
|
|
|
|
// 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->_number = $params['card']['number'];
|
|
$this->_exp_month = $params['card']['month'];
|
|
$this->_exp_year = $params['card']['year'];
|
|
|
|
$subtotal = 0;
|
|
|
|
foreach ($params['cart'] as $d) {
|
|
$dish = new Order_Dish;
|
|
$dish->id_dish = $d['id'];
|
|
$subtotal += $dish->dish()->price;
|
|
if ($d['options']) {
|
|
foreach ($d['options'] as $o) {
|
|
$option = new Order_Dish_Option;
|
|
$option->id_option = $o;
|
|
$subtotal += $option->option()->price;
|
|
// $subtotal += $option->option()->optionPrice($d['options']);
|
|
$dish->_options[] = $option;
|
|
}
|
|
}
|
|
|
|
$this->_dishes[] = $dish;
|
|
}
|
|
|
|
$this->id_restaurant = $params['restaurant'];
|
|
|
|
// price
|
|
$this->price = Util::ceil($subtotal, 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);
|
|
|
|
// tip
|
|
$this->tip = $params['tip'];
|
|
$this->tip_type = self::TIP_NUMBER;
|
|
$tip = $this->tip;
|
|
|
|
/******************
|
|
$tip = ($this->price * ($this->tip/100));
|
|
$tip = Util::ceil($tip, 2);
|
|
********/
|
|
|
|
// tax
|
|
$this->tax = $this->restaurant()->tax;
|
|
$tax = $totalWithFees * ($this->tax/100);
|
|
$tax = Util::ceil($tax, 2);
|
|
|
|
$this->final_price = Util::ceil($totalWithFees + $tip + $tax, 2); // price
|
|
|
|
$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.';
|
|
|
|
$DeLorean = new TimeMachine($this->restaurant()->timezone);
|
|
$debug = [
|
|
'type' => 'closed',
|
|
'now' => $DeLorean->now(),
|
|
'cart' => $params['cart'],
|
|
'params' => $params,
|
|
'restaurant' => $this->restaurant()->exports(['categories' => true, 'notifications' => true]),
|
|
];
|
|
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) {
|
|
return $errors;
|
|
}
|
|
|
|
$res = $this->verifyPayment();
|
|
|
|
if ($res !== true) {
|
|
return $res['errors'];
|
|
|
|
} else {
|
|
$this->txn = $this->transaction();
|
|
}
|
|
|
|
$user = c::user()->id_user ? c::user() : new User;
|
|
|
|
if (!c::user()->id_user) {
|
|
$user->active = 1;
|
|
}
|
|
if ($this->_customer->id) {
|
|
switch (c::config()->processor) {
|
|
case 'stripe':
|
|
default:
|
|
$user->stripe_id = $this->_customer->id;
|
|
break;
|
|
|
|
case 'balanced':
|
|
$user->balanced_id = $this->_customer->id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$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;
|
|
}
|
|
|
|
if ($this->pay_type == 'card' && $params['card']['number']) {
|
|
$user->card = str_repeat('*',strlen($params['card']['number'])-4).substr($params['card']['number'],-4);
|
|
$user->card_exp_year = $params['card']['year'];
|
|
$user->card_exp_month = $params['card']['month'];
|
|
}
|
|
|
|
$user->pay_type = $this->pay_type;
|
|
$user->delivery_type = $this->delivery_type;
|
|
$user->tip = $this->tip;
|
|
|
|
$this->env = c::env();
|
|
$this->processor = c::config()->processor;
|
|
|
|
$user->save();
|
|
$user = new User($user->id_user);
|
|
$this->_user = $user;
|
|
|
|
// 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 );
|
|
|
|
if ($this->_customer->id) {
|
|
switch (c::config()->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();
|
|
|
|
$this->id_user = $this->_user->id_user;
|
|
$this->date = date('Y-m-d H:i:s');
|
|
$this->id_community = $this->restaurant()->community()->id_community;
|
|
$this->save();
|
|
$this->que();
|
|
|
|
$this->debitFromUserCredit();
|
|
|
|
if ($params['make_default']) {
|
|
$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();
|
|
|
|
foreach ($dish->options() as $option) {
|
|
$option->id_order_dish = $dish->id_order_dish;
|
|
$option->save();
|
|
}
|
|
}
|
|
|
|
if ($params['make_default']) {
|
|
Preset::cloneFromOrder($this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
public function calcFinalPriceMinusUsersCredit(){
|
|
if( c::user()->id_user ){
|
|
$credit = Crunchbutton_Credit::creditByUserRestaurant( c::user()->id_user, $this->id_restaurant );
|
|
} else {
|
|
$credit = 0;
|
|
}
|
|
$final = $this->final_price - $credit;
|
|
if( $final < 0 ){
|
|
$final = 0;
|
|
}
|
|
return Util::ceil($final, 2);
|
|
}
|
|
|
|
public function charged(){
|
|
if( $this->id_credit ){
|
|
$credit = Crunchbutton_Credit::o( $this->id_credit );
|
|
if( $credit->id_credit ){
|
|
$charged = $this->final_price - $credit->value;
|
|
return $charged;
|
|
}
|
|
}
|
|
return $this->final_price;
|
|
}
|
|
|
|
public function debitFromUserCredit(){
|
|
if( c::user()->id_user ){
|
|
$credit = Crunchbutton_Credit::creditByUserRestaurant( c::user()->id_user, $this->id_restaurant );
|
|
} else {
|
|
$credit = 0;
|
|
}
|
|
// Debit from the user's credit
|
|
if( $credit > 0 ){
|
|
|
|
if( $this->final_price < $credit ){
|
|
$amount = $this->final_price;
|
|
$creditLeft = $credit - $this->final_price;
|
|
} else {
|
|
$amount = $credit;
|
|
$creditLeft = 0;
|
|
}
|
|
$amount = Util::ceil( $amount, 2);
|
|
|
|
$debit = new Crunchbutton_Credit();
|
|
$debit->id_user = $this->_user->id_user;
|
|
$debit->type = Crunchbutton_Credit::TYPE_DEBIT;
|
|
$debit->id_order = $this->id_order;
|
|
$debit->id_restaurant = $this->id_restaurant;
|
|
$debit->date = date('Y-m-d H:i:s');
|
|
$debit->value = $amount;
|
|
$debit->save();
|
|
if( $debit->id_credit ){
|
|
$order = Crunchbutton_Order::o( $this->id_order );
|
|
$order->id_credit = $debit->id_credit;
|
|
$order->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function uuid($uuid) {
|
|
return self::q('select * from `order` where uuid="'.$uuid.'"');
|
|
}
|
|
|
|
/**
|
|
* 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 transaction() {
|
|
return $this->_txn;
|
|
}
|
|
|
|
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 verifyPayment() {
|
|
switch ($this->pay_type) {
|
|
case 'cash':
|
|
$status = true;
|
|
break;
|
|
|
|
case 'card':
|
|
$user = c::user()->id_user ? c::user() : null;
|
|
switch (c::config()->processor) {
|
|
case 'stripe':
|
|
default:
|
|
$charge = new Charge_Stripe([
|
|
'stripe_id' => $user->stripe_id
|
|
]);
|
|
break;
|
|
|
|
case 'balanced':
|
|
$charge = new Charge_Balanced([
|
|
'balanced_id' => $user->balanced_id
|
|
]);
|
|
break;
|
|
}
|
|
|
|
$amount = $this->calcFinalPriceMinusUsersCredit();
|
|
// If the amount is 0 it means that the user used his credit.
|
|
if( $amount > 0 ){
|
|
$r = $charge->charge([
|
|
'amount' => $amount,
|
|
'number' => $this->_number,
|
|
'exp_month' => $this->_exp_month,
|
|
'exp_year' => $this->_exp_year,
|
|
'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'];
|
|
$status = true;
|
|
} else {
|
|
$status = $r;
|
|
}
|
|
} else {
|
|
$status = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
public static function recent() {
|
|
return self::q('select * from `order` order by `date` DESC');
|
|
}
|
|
|
|
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').'" ';
|
|
}
|
|
|
|
if ($search['restaurant']) {
|
|
$query .= ' and `order`.id_restaurant="'.$search['restaurant'].'" ';
|
|
}
|
|
|
|
if ($search['community']) {
|
|
$query .= ' and `order`.id_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`.address like "%'.$word.'%"
|
|
or `restaurant`.name like "%'.$word.'%"
|
|
or `order`.phone like "%'.$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 {
|
|
return number_format($this->price * ($this->tip/100),2);
|
|
}
|
|
}
|
|
|
|
public function tax() {
|
|
return number_format(($this->price + $this->deliveryFee() + $this->serviceFee()) * ($this->tax/100),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 notify() {
|
|
foreach ($this->restaurant()->notifications() as $n) {
|
|
/* @var $n Crunchbutton_Notification */
|
|
$n->send($this);
|
|
}
|
|
}
|
|
|
|
public function confirm() {
|
|
|
|
if ($this->confirmed || !$this->restaurant()->confirmation) {
|
|
return;
|
|
}
|
|
|
|
$env = c::env() == 'live' ? 'live' : 'dev';
|
|
$num = ($env == 'live' ? $this->restaurant()->phone : c::config()->twilio->testnumber);
|
|
|
|
$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();
|
|
|
|
/*
|
|
$num = '_PHONE_';
|
|
$_SERVER['__HTTP_HOST'] = 'dev.crunchr.co';
|
|
*/
|
|
|
|
$callback = 'http://'.$_SERVER['__HTTP_HOST'].'/api/notification/'.$log->id_notification_log.'/confirm';
|
|
|
|
Log::debug([
|
|
'order' => $this->id_order,
|
|
'action' => 'dial confirm call',
|
|
'num' => $num,
|
|
'host' => $_SERVER['__HTTP_HOST'],
|
|
'callback' => $callback,
|
|
'type' => 'notification'
|
|
]);
|
|
|
|
$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,
|
|
'http://'.$_SERVER['__HTTP_HOST'].'/api/order/'.$this->id_order.'/doconfirm',
|
|
[
|
|
'StatusCallback' => $callback
|
|
// 'IfMachine' => 'Hangup'
|
|
]
|
|
);
|
|
|
|
$log->remote = $call->sid;
|
|
$log->status = $call->status;
|
|
$log->save();
|
|
}
|
|
|
|
public function receipt() {
|
|
$env = c::env() == 'live' ? 'live' : 'dev';
|
|
//$num = ($env == 'live' ? $this->phone : c::config()->twilio->testnumber);
|
|
$num = $this->phone;
|
|
|
|
$twilio = new Twilio(c::config()->twilio->{$env}->sid, c::config()->twilio->{$env}->token);
|
|
$message = str_split($this->message('selfsms'),160);
|
|
|
|
$type = 'twilio';
|
|
|
|
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() {
|
|
$order = $this;
|
|
Cana::timeout(function() use($order) {
|
|
/* @var $order Crunchbutton_Order */
|
|
$order->notify();
|
|
});
|
|
|
|
if (!$this->restaurant()->confirmation) {
|
|
c::timeout(function() use($order) {
|
|
$order->receipt();
|
|
}, 30 * 1000); // 30 seconds
|
|
}
|
|
}
|
|
|
|
public function queConfirm() {
|
|
$order = $this;
|
|
if ($this->confirmed || !$this->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="'.$this->id_order.'" and type = "confirm" and ( status = "queued" or status = "queued" ) ');
|
|
if( $nl->count() > 0 ){
|
|
return;
|
|
}
|
|
Log::debug([
|
|
'order' => $this->id_order,
|
|
'action' => 'confirm qued',
|
|
'type' => 'notification'
|
|
]);
|
|
|
|
// If restaurant has fax notification we should wait 5 min before send the confirmation #784
|
|
if( $this->restaurant()->hasFaxNotification() ){
|
|
$confirmationTime = c::config()->twilio->confirmFaxTime;
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'hasFaxNotification - confirmationTime :' . $confirmationTime, 'type' => 'notification' ]);
|
|
} else {
|
|
$confirmationTime = c::config()->twilio->confirmTime;
|
|
Log::debug([ 'order' => $this->id_order, 'action' => 'confirmationTime :' . $confirmationTime, 'type' => 'notification' ]);
|
|
}
|
|
|
|
c::timeout(function() use($order) {
|
|
$order->confirm();
|
|
}, $confirmationTime, false);
|
|
}
|
|
|
|
public function orderMessage($type) {
|
|
|
|
switch ($type) {
|
|
case 'sms':
|
|
case 'web':
|
|
$with = 'w/';
|
|
$space = ',';
|
|
break;
|
|
|
|
case 'phone':
|
|
$with = '. ';
|
|
$space = '.';
|
|
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);
|
|
|
|
// Facebook does not share the options
|
|
if( $type == 'facebook' ){
|
|
$foodItem .= '. ';
|
|
$food .= $foodItem;
|
|
continue;
|
|
}
|
|
|
|
$options = $dish->options();
|
|
|
|
if (gettype($options) == 'array') {
|
|
$options = i::o($options);
|
|
}
|
|
|
|
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).'. ';
|
|
|
|
} else {
|
|
$foodItem .= '. ';
|
|
}
|
|
|
|
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) {
|
|
|
|
$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";
|
|
$msg .= "Contact ".$this->restaurant()->shortName().": ".$this->restaurant()->phone().".\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 '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 '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') {
|
|
if ($this->id_credit) {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer has already paid for this order by credit card and gift card.';
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The gift card value was ' . $this->phoeneticNumber( $this->credit()->value ) . '.';
|
|
if( $this->charged() > 0 ){
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The total charged was ' . $this->phoeneticNumber( $this->charged() ) . '.';
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">No value was charged from user\'s credit card.';
|
|
}
|
|
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer has already paid for this order by credit card.';
|
|
}
|
|
|
|
} else {
|
|
if ($this->id_credit) {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer will pay for this order with cash and gift card.';
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer used a gift card value ' . $this->phoeneticNumber( $this->credit()->value ) . '.';
|
|
if( $this->charged() > 0 ){
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">TThe total charged value should be ' . $this->phoeneticNumber( $this->charged() ) . '.';
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">No value should be charged from user.';
|
|
}
|
|
} else {
|
|
$msg .= '</Say><Pause length="1" /><Say voice="'.c::config()->twilio->voice.'">The customer will pay for this order with cash.';
|
|
}
|
|
}
|
|
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']);
|
|
|
|
$out['_restaurant_name'] = $this->restaurant()->name;
|
|
$out['user'] = $this->user()->uuid;
|
|
$out['_message'] = nl2br($this->orderMessage('web'));
|
|
$out['charged'] = $this->charged();
|
|
if( $this->id_credit ){
|
|
$credit = Crunchbutton_Credit::o( $this->id_credit );
|
|
$out['credit'] = $credit->value;
|
|
} else {
|
|
$out['credit'] = 0;
|
|
}
|
|
|
|
$timezone = new DateTimeZone($this->restaurant()->timezone);
|
|
|
|
$date = new DateTime($this->date);
|
|
$date->setTimeZone($timezone);
|
|
|
|
$out['_date_tz'] = $date->format('Y-m-d H:i:s');
|
|
$out['_tz'] = $date->format('T');
|
|
|
|
return $out;
|
|
}
|
|
|
|
public function refundGiftFromOrder(){
|
|
if( $this->id_credit ){
|
|
$value = $this->credit()->value;
|
|
$gift = new Crunchbutton_Credit();
|
|
$gift->id_user = $this->id_user;
|
|
$gift->type = Crunchbutton_Credit::TYPE_CREDIT;
|
|
$gift->id_restaurant = $this->id_restaurant;
|
|
$gift->date = date('Y-m-d H:i:s');
|
|
$gift->value = $value;
|
|
$gift->note = 'Refunded from order: ' . $this->id_order . '.';
|
|
$gift->save();
|
|
}
|
|
}
|
|
|
|
public function refund() {
|
|
if( $this->refunded < 1 ){
|
|
// Refund the gift
|
|
$this->refundGiftFromOrder();
|
|
|
|
if( $this->charged() > 0 ){
|
|
$env = c::env() == 'live' ? 'live' : 'dev';
|
|
switch ($this->processor) {
|
|
case 'stripe':
|
|
default:
|
|
Stripe::setApiKey(c::config()->stripe->{$env}->secret);
|
|
$ch = Stripe_Charge::retrieve($this->txn);
|
|
try {
|
|
$ch->refund();
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 'balanced':
|
|
$ch = Crunchbutton_Balanced_Debit::byId($this->txn);
|
|
try {
|
|
$ch->refund();
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
$this->refunded = 1;
|
|
$this->save();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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 order by id_order desc limit 0,1');
|
|
}
|
|
|
|
public function lastTipByDelivery($id_user = null, $delivery ) {
|
|
$id_user = ( $id_user ) ? $id_user : $this->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 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 credit() {
|
|
return Crunchbutton_Credit::o( $this->id_credit );
|
|
}
|
|
|
|
public function community() {
|
|
return Community::o($this->id_community);
|
|
}
|
|
|
|
public function __construct($id = null) {
|
|
parent::__construct();
|
|
$this
|
|
->table('order')
|
|
->idVar('id_order')
|
|
->load($id);
|
|
}
|
|
} |