implement additional payment math in Settlement.php #2250

- payment summary page
This commit is contained in:
Daniel Camargo 2014-06-16 17:47:08 -03:00
parent 1a544d2cb3
commit e9aa7b41b9
11 changed files with 331 additions and 39 deletions

View File

@ -55,6 +55,9 @@ class Controller_api_settlement extends Crunchbutton_Controller_RestAccount {
case 'schedule':
$this->_restaurantSchedule();
break;
case 'payment':
$this->_restaurantPayment();
break;
case 'status':
$this->_restaurantStatus();
break;
@ -81,6 +84,17 @@ class Controller_api_settlement extends Crunchbutton_Controller_RestAccount {
}
}
private function _restaurantPayment(){
$id_payment_schedule = c::getPagePiece( 4 );
$settlement = new Settlement;
$summary = $settlement->restaurantSummary( $id_payment_schedule );
if( $summary ){
echo json_encode( $summary );
} else {
$this->_error();
}
}
private function _restaurantSchedule(){
$start = $this->request()['start'];
$end = $this->request()['end'];

View File

@ -31,6 +31,15 @@ class Cockpit_Payment_Schedule extends Cana_Table {
return $out;
}
public function payment(){
if( $this->id_payment ){
return Crunchbutton_Payment::o( $this->id_payment );
}
}
public function admin() {
return Admin::o( $this->id_admin );
}
public function restaurant() {
return Restaurant::o( $this->id_restaurant );
@ -44,7 +53,7 @@ class Cockpit_Payment_Schedule extends Cana_Table {
}
public function orders(){
return Cockpit_Payment_Schedule_Order::q( 'SELECT * FROM payment_schedule_order WHERE id_payment_schedule = "' . $this->id_payment_schedule . '"' );
return Cockpit_Payment_Schedule_Order::q( 'SELECT * FROM payment_schedule_order WHERE id_payment_schedule = "' . $this->id_payment_schedule . '" ORDER BY id_order DESC' );
}
}

View File

@ -21,6 +21,6 @@ class Cockpit_Payment_Schedule_Order extends Cana_Table {
}
public function order() {
return Cockpit_Order::o($this->order);
return Cockpit_Order::o( $this->id_order );
}
}

View File

@ -65,13 +65,16 @@ class Crunchbutton_Settlement extends Cana_Model {
// this method receives the restaurant orders and run the math
public function restaurantsProcessOrders( $orders ){
public function restaurantsProcessOrders( $orders, $recalculatePaidOrders = false ){
// start all with 0
$pay = [ 'card_subtotal' => 0, 'cash_reimburse' => 0, 'tax' => 0, 'delivery_fee' => 0, 'tip' => 0, 'customer_fee' => 0, 'markup' => 0, 'credit_charge' => 0, 'restaurant_fee' => 0, 'promo_gift_card' => 0, 'apology_gift_card' => 0, 'order_payment' => 0, 'cash_subtotal' => 0 ];
foreach ( $orders as $order ) {
if( $order ){
// Pay if Refunded
if( ( $order[ 'refunded' ] == 1 && $order[ 'pay_if_refunded' ] == 0 ) || $order[ 'restaurant_paid' ] ){
if( ( $order[ 'refunded' ] == 1 && $order[ 'pay_if_refunded' ] == 0 ) ){
continue;
}
if( $order[ 'restaurant_paid' ] && !$recalculatePaidOrders ){
continue;
}
$pay[ 'card_subtotal' ] += $this->orderCardSubtotalPayment( $order );
@ -90,6 +93,9 @@ class Crunchbutton_Settlement extends Cana_Model {
$pay[ 'formal_relationship' ] = $order[ 'formal_relationship' ];
}
}
foreach ( $pay as $key => $val ) {
// $pay[ $key ] = Util::round_up( number_format( $val, 3 ), 2 );
}
// sum
$pay[ 'total_due' ] = $this->orderCalculateTotalDue( $pay );
return $pay;
@ -367,6 +373,7 @@ class Crunchbutton_Settlement extends Cana_Model {
$values[ 'pay_type' ] = $order->pay_type;
$values[ 'restaurant' ] = $order->restaurant()->name;
$values[ 'date' ] = $order->date()->format( 'M jS Y g:i:s A' );
$values[ 'short_date' ] = $order->date()->format( 'M jS Y' );
return $values;
}
@ -431,7 +438,48 @@ class Crunchbutton_Settlement extends Cana_Model {
}
}
function payRestaurant( $id_payment_schedule ){
public function restaurantSummary( $id_payment_schedule ){
$schedule = Cockpit_Payment_Schedule::o( $id_payment_schedule );
if( $schedule->id_payment_schedule && $schedule->type == Cockpit_Payment_Schedule::TYPE_RESTAURANT ){
$settlement = new Settlement;
$summary = $schedule->exports();
$summary[ 'restaurant' ] = $schedule->restaurant()->name;
$payment = $schedule->payment();
if( $payment->id_payment ){
$summary[ 'balanced_id' ] = $payment->balanced_id;
$summary[ 'stripe_id' ] = $payment->stripe_id;
$summary[ 'payment_date' ] = $payment->date()->format( 'M jS Y g:i:s A T' );
}
$orders = $schedule->orders();
$_orders = [];
$summary[ 'orders_cash' ] = 0;
$summary[ 'orders_card' ] = 0;
$summary[ 'orders' ] = [ 'card' => [], 'cash' => [] ];
foreach( $orders as $order ){
$_order = $order->order();
if( $_order->id_order ){
$variables = $settlement->orderExtractVariables( $_order );
$type = $variables[ 'cash' ] ? 'cash' : 'card';
if( $type == 'cash' || ( $type == 'card' && $order->amount > 0 ) ){
if( $type == 'card' ){
$summary[ 'orders_card' ]++;
} else {
$summary[ 'orders_cash' ]++;
}
$summary[ 'orders' ][ $type ][] = [ 'id_order' => $variables[ 'id_order' ], 'name' => $variables[ 'name' ], 'total' => $variables[ 'final_price_plus_delivery_markup' ], 'date' => $variables[ 'short_date' ], 'tip' => $variables[ 'tip' ] ];
$_orders[] = $variables;
}
}
}
$summary[ 'calcs' ] = $settlement->restaurantsProcessOrders( $_orders, true );
$summary[ 'admin' ] = [ 'id_admin' => $schedule->id_admin, 'name' => $schedule->admin()->name ];
return $summary;
} else {
return false;
}
}
public function payRestaurant( $id_payment_schedule ){
$env = c::getEnv();
$schedule = Cockpit_Payment_Schedule::o( $id_payment_schedule );

View File

@ -3,18 +3,18 @@
class Crunchbutton_Util extends Cana_Model {
public function frontendTemplates($export = false) {
$files = [];
$themes = c::view()->theme();
$themes = array_reverse($themes);
foreach ($themes as $theme) {
if (file_exists(c::config()->dirs->view.$theme.'/frontend')) {
$frontendDir = $theme;
break;
}
}
foreach (new DirectoryIterator(c::config()->dirs->view.$frontendDir.'/frontend') as $fileInfo) {
if (!$fileInfo->isDot() && $fileInfo->getBasename() != '.DS_Store' ) {
$files[] = $fileInfo->getBasename('.phtml');
@ -60,10 +60,16 @@ class Crunchbutton_Util extends Cana_Model {
}
public static function ceil($value, $precision) {
$pow = pow ( 10, $precision );
$pow = pow ( 10, $precision );
return ( ceil ( $pow * $value ) + ceil ( $pow * $value - ceil ( $pow * $value ) ) ) / $pow;
}
public static function round_up ($value, $places=0) {
if ($places < 0) { $places = 0; }
$mult = pow(10, $places);
return ceil($value * $mult) / $mult;
}
public static function encodeTitle($title) {
$find = array(
@ -84,7 +90,7 @@ class Crunchbutton_Util extends Cana_Model {
return strtolower(urlencode(trim(self::truncateByWord($title,50,''))));
}
static function uploadWWW(){
return '/upload/';
}
@ -101,12 +107,12 @@ class Crunchbutton_Util extends Cana_Model {
static public function allowedExtensionUpload( $ext ){
$ext = strtolower( $ext );
$allowed = array( 'gif','png' ,'jpg', 'doc', 'docx', 'pdf', 'jpeg' );
$allowed = array( 'gif','png' ,'jpg', 'doc', 'docx', 'pdf', 'jpeg' );
return in_array( $ext, $allowed );
}
static public function slugify( $txt ){
static public function slugify( $txt ){
$table = array(
'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
@ -136,7 +142,7 @@ class Crunchbutton_Util extends Cana_Model {
$file = explode(' ',$file[0]);
return trim($file[1]);
}
public static function relativeTime($timestamp, $timezoneTO = null, $timezoneFROM = null) {
if (!$timezoneFROM) {
// $timezoneFROM = new DateTimeZone('utc');
@ -159,9 +165,9 @@ class Crunchbutton_Util extends Cana_Model {
$difference = $interval->format('%y %m %d %h %i %s');
return $difference;
*/
$difference = $t->getTimestamp() - $d->getTimestamp();
$periods = ['sec', 'min', 'hour', 'day', 'week', 'month', 'year', 'decade'];
$lengths = ['60','60','24','7','4.35','12','10'];
@ -170,7 +176,7 @@ class Crunchbutton_Util extends Cana_Model {
} else { // this was in the future
$difference = -$difference;
$ending = 'to go';
}
}
for($j = 0; $difference >= $lengths[$j]; $j++) {
$difference /= $lengths[$j];
}
@ -193,7 +199,7 @@ class Crunchbutton_Util extends Cana_Model {
}
return implode("\n",$retVals);
}
public static function truncateByWord($string, $length = 100, $suffix = '&hellip;') {
if (strlen($string) > $length) {
$string = explode(' ', $string);
@ -208,13 +214,13 @@ class Crunchbutton_Util extends Cana_Model {
$string = trim($newString).$suffix;
}
return $string;
}
public function relativeTimeTz($ts, $tz) {
}
public function dateTimeRep($datetime, $timezome, $format = 'M j, g:i a') {
$d = new DateTime($datetime_str, new DateTimeZone('utc'));
$d->setTimezone($rep_timezone);

View File

@ -0,0 +1,175 @@
<div class="top-pad"></div>
<div class="content-padding" ng-if="ready">
<h1 class="title left"><i class="fa fa-bank"></i><span>Settlement:: Restaurants:: Payment</span></h1>
<div class="divider"></div>
<h2 class="title">{{result.restaurant}}</h2>
<table class="tb-grid tb-zebra">
<tbody>
<tr ng-show="result.id_payment">
<td><strong>Payment ID</strong></td>
<td># {{result.id_payment}}</td>
</tr>
<tr>
<td><strong>Payment Status</strong></td>
<td>{{result.status}}</td>
</tr>
<tr ng-show="result.payment_date">
<td><strong>Payment Date</strong></td>
<td>{{result.payment_date}}</td>
</tr>
<tr class="transparent"><td></td><td></td></tr>
<tr ng-show="result.balanced_id">
<td>Balanced</td>
<td>{{result.balanced_id}}</td>
</tr>
<tr ng-show="result.balanced_id">
<td>Stripe</td>
<td>{{result.stripe_id}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.tax}}">
<td>Tax</td>
<td>$ {{result.calcs.tax | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.delivery_fee}}">
<td>Delivery Fee</td>
<td>$ {{result.calcs.delivery_fee | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.tip}}">
<td>Tip</td>
<td>$ {{result.calcs.tip | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.promo_gift_card}}">
<td>Promo Gift Card</td>
<td>$ {{result.calcs.promo_gift_card | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.apology_gift_card}}">
<td>Apology Gift Card</td>
<td>$ {{result.calcs.apology_gift_card | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.card_subtotal}}">
<td>Credit Card Subtotal</td>
<td>$ {{result.calcs.card_subtotal | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.cash_reimburse}}">
<td>Cash Reimburse</td>
<td>$ {{result.calcs.cash_reimburse | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.cash_subtotal}}">
<td>Cash Subtotal</td>
<td>$ {{result.calcs.cash_subtotal | formatPrice}}</td>
</tr>
<tr positive-or-negative-color="{{result.calcs.credit_charge}}">
<td>Credit Card Charge</td>
<td>$ {{result.calcs.credit_charge | formatPrice}}</td>
</tr>
<tr class="total-due" positive-or-negative-color="{{result.calcs.total_due}}">
<td class="td-medium">Total due</td>
<td class="td-medium">$ {{result.calcs.total_due | formatPrice}}</td>
</tr>
<tr>
<td>Payment Notes</td>
<td>{{result.calcs.notes}}</td>
</tr>
</tbody>
</table>
<br/>
<table class="tb-grid tb-zebra">
<thead>
<tr>
<td colspan="4" class="title">Card Orders</td>
</tr>
</thead>
<thead>
<tr>
<td class="td-medium">Curstomer name</td>
<td>Date</td>
<td>Total</td>
<td>Tip</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="order in result.orders.card">
<td>{{order.name}}</td>
<td>{{order.date}}</td>
<td>$ {{order.total | formatPrice}}</td>
<td>$ {{order.tip | formatPrice}}</td>
</tr>
</tbody>
<tbody>
<tr class="transparent">
<td></td>
<td class="highlight-1">Subtotal</td>
<td class="highlight-1">$ {{result.calcs.order_payment | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent">
<td></td>
<td class="highlight-1">Tips by Card</td>
<td class="highlight-1">$ {{result.calcs.tip | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent">
<td></td>
<td class="highlight-1">Visa/Mastercard/Amex Charges</td>
<td class="highlight-1">$ {{result.calcs.credit_charge | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent"><td></td><td></td><td></td><td></td></tr>
</tbody>
<thead>
<tr>
<td colspan="4" class="title">Cash Orders</td>
</tr>
</thead>
<thead>
<tr>
<td class="td-medium">Curstomer name</td>
<td>Date</td>
<td>Total</td>
<td></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="order in result.orders.cash">
<td>{{order.name}}</td>
<td>{{order.date}}</td>
<td>$ {{order.total | formatPrice}}</td>
<td></td>
</tr>
</tbody>
<tbody>
<tr class="transparent">
<td></td>
<td class="highlight-1">Subtotal</td>
<td class="highlight-1">$ {{result.calcs.cash_subtotal | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent">
<td></td>
<td class="highlight-1">Fees</td>
<td class="highlight-1">$ {{result.calcs.restaurant_fee | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent">
<td></td>
<td class="highlight-2">Total Payment (minus fees)</td>
<td class="highlight-2">$ {{result.calcs.total_due | formatPrice}}</td>
<td></td>
</tr>
<tr class="transparent"><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
</div>
<spinner-loading></spinner-loading>
<hack-expand-content></hack-expand-content>

View File

@ -18,7 +18,7 @@
<span>Summary</span>
</h2>
<h3 class="title">
Updated at {{result.updated_at}}
Summary updated at {{result.updated_at}} <span class="orange link" ng-click="update()">Update now</span>
</h3>
<table class="tb-grid tb-zebra summary">
@ -53,7 +53,6 @@
<table class="tb-grid tb-zebra">
<thead>
<tr>
<td>#</td>
<td class="td-medium">Restaurant</td>
<td>Amount</td>
<td>Status</td>
@ -61,8 +60,9 @@
</thead>
<tbody>
<tr ng-repeat="restaurant in result.restaurants">
<td>{{restaurant.id_payment_schedule}}</td>
<td>{{restaurant.restaurant}}</td>
<td>
<span class="orange link" ng-click="payment(restaurant.id_payment_schedule)">{{restaurant.restaurant}}</span>
</td>
<td>$ {{restaurant.amount | formatPrice}}</td>
<td>
{{restaurant.status}}

View File

@ -94,6 +94,11 @@ NGApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $l
controller: 'SettlementRestaurantsStatusCtrl',
templateUrl: 'assets/view/settlement-restaurants-status.html'
})
.when('/settlement/restaurants/payment/:id', {
action: 'settlement',
controller: 'SettlementRestaurantsPaymentCtrl',
templateUrl: 'assets/view/settlement-restaurants-payment.html'
})
.when('/settlement/drivers', {
action: 'settlement',
controller: 'SettlementDriversCtrl',

View File

@ -191,26 +191,39 @@ NGApp.controller('SettlementRestaurantsStatusCtrl', function ( $scope, $timeout,
$scope.ready = false;
var scheduled_payments = function(){
$scope.update = function(){
SettlementService.restaurants.status( function( json ){
$scope.result = json;
$scope.ready = true;
var timer = $timeout( function() {
scheduled_payments();
}, 5000 );
} );
}
$scope.$on( '$destroy', function(){
// Kills the timer when the controller is changed
if( typeof( timer ) !== 'undefined' && timer ){
try{ $timeout.cancel( timer ); } catch(e){}
}
} );
$scope.payment = function( id_payment_schedule ){
$scope.navigation.link( '/settlement/restaurants/payment/' + id_payment_schedule );
}
// Just run if the user is loggedin
if( $scope.account.isLoggedIn() ){
scheduled_payments();
$scope.update();
}
});
NGApp.controller('SettlementRestaurantsPaymentCtrl', function ( $scope, $timeout, SettlementService ) {
$scope.ready = false;
load = function(){
SettlementService.restaurants.payment( function( json ){
$scope.result = json;
console.log('$scope.result',$scope.result);
$scope.ready = true;
} );
}
// Just run if the user is loggedin
if( $scope.account.isLoggedIn() ){
load();
}
});

View File

@ -1,4 +1,4 @@
NGApp.factory( 'SettlementService', function( $resource ) {
NGApp.factory( 'SettlementService', function( $resource, $routeParams ) {
var service = { restaurants : {}, drivers : {} };
var settlement = { restaurants : {}, drivers : {} };
@ -6,11 +6,12 @@ NGApp.factory( 'SettlementService', function( $resource ) {
service.pay_type_options = [ { 'name': 'All', 'value' : 'all' }, { 'name': 'Check', 'value' : 'check' }, { 'name': 'Deposit', 'value' : 'deposit' } ];
service.sort_options = [ { 'name': 'Last Payment', 'value' : 'last_payment' }, { 'name': 'Alphabetical', 'value' : 'alphabetical' } ];
settlement.restaurants = $resource( App.service + 'settlement/restaurants/:action/', { action: '@action' }, {
settlement.restaurants = $resource( App.service + 'settlement/restaurants/:action/:id_payment_schedule', { action: '@action', id_payment_schedule: '@id_payment_schedule' }, {
'range' : { 'method': 'GET', params : { action: 'range' } },
'begin' : { 'method': 'POST', params : { action: 'begin' } },
'restaurant' : { 'method': 'POST', params : { action: 'restaurant' } },
'pay_if_refunded' : { 'method': 'POST', params : { action: 'pay-if-refunded' } },
'payment' : { 'method': 'POST', params : { action: 'payment' } },
'reimburse_cash_order' : { 'method': 'POST', params : { action: 'reimburse-cash-order' } },
'schedule' : { 'method': 'POST', params : { action: 'schedule' } },
'status' : { 'method': 'POST', params : { action: 'status' } }
@ -39,6 +40,12 @@ NGApp.factory( 'SettlementService', function( $resource ) {
} );
}
service.restaurants.payment = function( callback ){
settlement.restaurants.payment( { 'id_payment_schedule' : $routeParams.id }, function( json ){
callback( json );
} );
}
service.restaurants.pay_if_refunded = function( params, callback ){
settlement.restaurants.pay_if_refunded( params, function( json ){
callback( json );

View File

@ -781,6 +781,21 @@ input {
font-style: normal;
text-decoration: underline !important;
}
.tb-zebra .title{
background: #CCC;
font-weight: bold;
}
.tb-zebra tr.transparent{
background: #f1f2f7 !important;
}
.tb-zebra .highlight-1{
background: #5cd3c7;
}
.tb-zebra .highlight-2{
background: #4db1a7;
font-weight: bold;
}
.tb-hack tbody {
width: 100% !important;