aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--classes/Mail.php3
-rw-r--r--classes/Model.php36
-rw-r--r--classes/Offer.php41
-rw-r--r--classes/Payment.php91
-rw-r--r--conf.php6
-rw-r--r--include/offers-view.php40
-rw-r--r--include/offers.php41
7 files changed, 148 insertions, 110 deletions
diff --git a/classes/Mail.php b/classes/Mail.php
index dcc5cf5..06c2eb6 100644
--- a/classes/Mail.php
+++ b/classes/Mail.php
@@ -28,5 +28,6 @@ class Mail extends Model {
/** {@inheritDoc} */
public
$table = 'mail',
- $fillable_columns = ['contactId', 'offerId', 'subject'];
+ $fillable_columns = ['contactId', 'offerId', 'subject'],
+ $timestamps = ['date'];
}
diff --git a/classes/Model.php b/classes/Model.php
index 6ee7821..3b1c49e 100644
--- a/classes/Model.php
+++ b/classes/Model.php
@@ -45,15 +45,21 @@ class ModelSetFailedException extends Exception {
abstract class Model {
/**
* @var string $table The database table
+ * @var string $primary_key The table's primary key
* @var string[] $protected_columns Columns that cannot be edited
* @var string[] $fillable_columns Columns that can be edited
- * @var string $primary_key The table's primary key
+ * @var string[] $timestamps Columns that are TIMESTAMPs (special treatment in accessor and mutator)
+ * @var string[] $dates Columns that are DATEs (special treatment in accessor and mutator)
+ * @var string[] $booleans Columns that are BOOLEANs (special treatment in accessor and mutator)
*/
public
$table = '',
+ $primary_key = 'id',
$protected_columns = ['id'],
$fillable_columns = [],
- $primary_key = 'id';
+ $timestamps = [],
+ $dates = [],
+ $booleans = [];
/**
* @var PDO $pdo A PDO instance for database communication
@@ -91,7 +97,7 @@ abstract class Model {
*/
public function __set($key, $value) {
if (!in_array($key, $this->fillable_columns)) {
- throw new ModelIllegalAccessException("Column $key cannot be edited.");
+ throw new ModelIllegalAccessException("Column `{$this->table()}`.`$key` cannot be edited.");
}
if ($this->data[$key] == $value) {
return;
@@ -102,7 +108,7 @@ abstract class Model {
$this->data[$this->primary_key]
]);
if ($stmt->rowCount() != 1) {
- throw new ModelEditFailedException();
+ throw new ModelEditFailedException("Failed to update `{$this->table()}`.`$key` to '$value'.");
}
$this->data[$key] = $value;
}
@@ -124,10 +130,18 @@ abstract class Model {
* @param string $key The column
* @param string $value The value
*
- * @return mixed The modified value (default: $value)
+ * @return mixed The modified value
*/
protected function accessor($key, $value) {
- return $value;
+ if (is_null($value)) {
+ return null;
+ } elseif (in_array($key, $this->booleans)) {
+ return (bool) $value;
+ } elseif (in_array($key, $this->dates) || in_array($key, $this->timestamps)) {
+ return strtotime($value);
+ } else {
+ return $value;
+ }
}
/**
@@ -136,10 +150,16 @@ abstract class Model {
* @param string $key The column
* @param mixed $value The value
*
- * @return string The modified value (default: $value casted to string)
+ * @return string The modified value
*/
protected function mutator($key, $value) {
- return (string) $value;
+ if (in_array($key, $this->dates) && is_int($value)) {
+ return date('Y-m-d', $value);
+ } elseif (in_array($key, $this->timestamps) && is_int($value)) {
+ return date('Y-m-d H:i:s', $value);
+ } else {
+ return (string) $value;
+ }
}
/**
diff --git a/classes/Offer.php b/classes/Offer.php
index 2a48604..202c0e8 100644
--- a/classes/Offer.php
+++ b/classes/Offer.php
@@ -28,44 +28,9 @@ class Offer extends Model{
/** {@inheritDoc} */
public
$table = 'offer',
- $fillable_columns = ['contactId', 'start_date', 'end_date', 'invoice_date', 'accepted', 'invoice_fileId', 'payment_key'];
-
- /**
- * {@inheritDoc}
- * @param $key {@inheritDoc}
- * @param $value {@inheritDoc}
- */
- protected function accessor($key, $value) {
- switch ($key) {
- case 'start_date':
- case 'end_date':
- case 'invoice_date':
- return strtotime($value);
- break;
- case 'accepted':
- return (bool) $value;
- case 'invoice_fileId':
- return $value == null ? null : (int) $value;
- default:
- return parent::accessor($key, $value);
- }
- }
-
- /**
- * {@inheritDoc}
- * @param $key {@inheritDoc}
- * @param $value {@inheritDoc}
- */
- protected function mutator($key, $value) {
- switch ($key) {
- case 'start_date':
- case 'end_date':
- case 'invoice_date':
- return is_string($value) ? $value : date('Y-m-d', $value);
- default:
- return parent::mutator($key, $value);
- }
- }
+ $fillable_columns = ['contactId', 'start_date', 'end_date', 'invoice_date', 'accepted', 'invoice_fileId', 'payment_key'],
+ $dates = ['start_date', 'end_date', 'invoice_date'],
+ $booleans = ['accepted'];
/**
* A random max-63-char string that can be used as payment_key
diff --git a/classes/Payment.php b/classes/Payment.php
index 64b65ae..bab55be 100644
--- a/classes/Payment.php
+++ b/classes/Payment.php
@@ -28,7 +28,8 @@ class Payment extends Model {
/** {@inheritDoc} */
public
$table = 'payment',
- $fillable_columns = ['offerId', 'date', 'braintree_id'];
+ $fillable_columns = ['offerId', 'date', 'braintree_id', 'braintree_status'],
+ $timestamps = ['date'];
/**
* Get the offer that this payment is linked to
@@ -40,36 +41,6 @@ class Payment extends Model {
}
/**
- * {@inheritDoc}
- * @param $key {@inheritDoc}
- * @param $value {@inheritDoc}
- */
- protected function accessor($key, $value) {
- switch ($key) {
- case 'date':
- return strtotime($value);
- break;
- default:
- return parent::accessor($key, $value);
- }
- }
-
- /**
- * {@inheritDoc}
- * @param $key {@inheritDoc}
- * @param $value {@inheritDoc}
- */
- protected function mutator($key, $value) {
- switch ($key) {
- case 'date':
- return is_string($value) ? $value : date('Y-m-d H:i:s', $value);
- break;
- default:
- return parent::mutator($key, $value);
- }
- }
-
- /**
* Make a mailer to send about this payment
*
* @return Mailer The mailer
@@ -92,4 +63,62 @@ class Payment extends Model {
public function send() {
return $this->mailer()->send();
}
+
+ /**
+ * Refresh the Braintree status
+ *
+ * @return string|null The new status, or null if this Payment does not have a Braintree id
+ */
+ public function refreshBraintreeStatus() {
+ if (is_null($this->braintree_id)) {
+ return null;
+ }
+
+ $trans = Braintree_Transaction::find($this->braintree_id);
+ $this->braintree_status = $trans->status;
+ }
+
+ /**
+ * Is the Braintree transaction finished?
+ *
+ * @return bool|null True iff the Braintree transaction is finished, or null if this Payment does not have a Braintree id
+ */
+ public function isBraintreeFinished() {
+ if (is_null($this->braintree_id)) {
+ return null;
+ }
+
+ return in_array($this->braintree_status, [
+ Braintree_Transaction::AUTHORIZATION_EXPIRED,
+ Braintree_Transaction::GATEWAY_REJECTED,
+ Braintree_Transaction::FAILED,
+ Braintree_Transaction::PROCESSOR_DECLINED,
+ Braintree_Transaction::SETTLED,
+ Braintree_Transaction::VOIDED,
+ Braintree_Transaction::SETTLEMENT_DECLINED,
+ ]);
+ }
+
+ /**
+ * Is the Braintree transaction successful?
+ *
+ * A transaction can be successful if it is not yet finished.
+ *
+ * @return bool|null True iff the Braintree transaction is successful, or null if this Payment does not have a Braintree id
+ */
+ public function isBraintreeSuccessful() {
+ if (is_null($this->braintree_id)) {
+ return null;
+ }
+
+ return in_array($this->braintree_status, [
+ Braintree_Transaction::AUTHORIZING,
+ Braintree_Transaction::AUTHORIZED,
+ Braintree_Transaction::SETTLED,
+ Braintree_Transaction::SETTLING,
+ Braintree_Transaction::SUBMITTED_FOR_SETTLEMENT,
+ Braintree_Transaction::SETTLEMENT_PENDING,
+ Braintree_Transaction::SETTLEMENT_CONFIRMED,
+ ]);
+ }
}
diff --git a/conf.php b/conf.php
index 9085b1e..4754a6d 100644
--- a/conf.php
+++ b/conf.php
@@ -33,6 +33,8 @@ ini_set('display_errors', 0);
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());
+require_once('conf.private.php');
+
spl_autoload_register(function ($pClass) {
$path = dirname(__FILE__) . "/classes/$pClass.php";
if (file_exists($path)) {
@@ -40,11 +42,11 @@ spl_autoload_register(function ($pClass) {
}
});
-require_once('conf.private.php');
-
try {
$_pdo = new PDO("mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_NAME.";charset=utf8", DB_USER, DB_PASS);
$_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $_pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
+ $_pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
} catch (PDOException $e) {
die("Down until PDO error fixed.");
}
diff --git a/include/offers-view.php b/include/offers-view.php
index ec579b6..1455e43 100644
--- a/include/offers-view.php
+++ b/include/offers-view.php
@@ -57,7 +57,7 @@ $_offer = new Offer($_pdo, $_id);
</tr>";
}
if (count($assignments) == 0) {
- echo "<tr><td colspan='6'>There are no assignments in the database. Why not start with creating one, below?</td></tr>";
+ echo "<tr><td colspan='5'>There are no assignments in the database. Why not start with creating one, below?</td></tr>";
}
?>
</tbody>
@@ -99,7 +99,7 @@ $_offer = new Offer($_pdo, $_id);
</tr>";
}
if (count($discounts) == 0) {
- echo "<tr><td colspan='6'>There are no discounts in the database. Why not start with creating one, below?</td></tr>";
+ echo "<tr><td colspan='4'>There are no discounts in the database. Why not start with creating one, below?</td></tr>";
}
?>
</tbody>
@@ -108,6 +108,42 @@ $_offer = new Offer($_pdo, $_id);
</div>
</div>
<div class="col-md-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">Payments</div>
+ <div class="panel-body table-responsive">
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Braintree ID</th>
+ <th>Braintree status</th>
+ <th>Tools</th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php
+ $payment = $_offer->getPayment();
+ $payments = [$payment];
+ foreach ($payments as $payment) {
+ echo "<tr>
+ <td>{$payment->id}</td>
+ <td>{$payment->braintree_id}</td>
+ <td>{$payment->braintree_status}</td>
+ <td class='col-min-width'>
+ <a title='Refresh' href='?id={$_offer->id}&refresh_payment={$payment->id}' class='btn btn-info btn-circle fa fa-refresh'></a>
+ </td>
+ </tr>";
+ }
+ if (count($payments) == 0) {
+ echo "<tr><td colspan='4'>There are no payments in the database. Why not start with creating one, below?</td></tr>";
+ }
+ ?>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+<div class="col-lg-12">
<div class="panel panel-default" id="panel-timeline">
<div class="panel-heading">
<i class="fa fa-clock-o fa-fw"></i> Timeline
diff --git a/include/offers.php b/include/offers.php
index e06a4eb..165d88f 100644
--- a/include/offers.php
+++ b/include/offers.php
@@ -37,7 +37,7 @@ require('./header.php');
// ?toggle_payment_eligibility=<id> Toggle the payment eligibility of the offer with id <id>
// ?generate_invoice=<id> Generate an invoice for the offer with id <id>
// ?trash_invoice=<id> Trash the invoice file
- // ?send_invoice=<id> Send invoice to contact
+ // ?refesh_payment=<id> Refresh the Braintree status of a payment
// ?delete=<id> Delete the offer with id <id>
//------------------------------------------------------------------------------
@@ -48,10 +48,8 @@ require('./header.php');
$offer = new Offer($_pdo, $id);
$offer->accepted = !$offer->accepted;
$alert = "<div class='alert alert-success alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The status of offer #{$offer->id} has been set to <i>".($offer->accepted ? "accepted" : "unaccepted")."</i>.</div>";
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The status of the offer could not be changed due to a PDO error.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer with id {$id} could not be found.</div>";
+ $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The acceptance status of offer {$id} could not be changed: {$e->getMessage()}.</div>";
}
}
@@ -66,10 +64,8 @@ require('./header.php');
$offer->payment_key = Offer::getRandomPaymentKey();
}
$alert = "<div class='alert alert-success alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer #{$offer->id} is now <i>".($offer->getPaymentEligibility() ? "eligible" : "ineligible")."</i> for online payment.</div>";
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The online payment eligibility could not be changed due to a PDO error.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer with id {$id} could not be found.</div>";
+ $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The payment eligibility of offer {$id} could not be changed: {$e->getMessage()}.</div>";
}
}
@@ -80,10 +76,8 @@ require('./header.php');
$offer = new Offer($_pdo, $id);
$file = $offer->generateInvoice();
$alert = "<div class='alert alert-success alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$offer->id} is generated: <a class='alert-link' href='{$file->getFilenameURI()}' target='_blank'>{$file->filename}</a></div>";
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$offer->id} could not be generated due to a PDO error.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$id} could not be generated.</div>";
+ $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$id} could not be generated: {$e->getMessage()}.</div>";
}
}
@@ -98,27 +92,20 @@ require('./header.php');
} else {
$alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$id} could not be trashed.</div>";
}
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$offer->id} could not be trashed due to a PDO error.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$id} could not be trashed.</div>";
+ $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer #{$id} could not be trashed: {$e->getMessage()}.</div>";
}
}
- // Send invoice
- if (isset($_GET['send_invoice'])) {
- $id = (int) $_GET['send_invoice'];
+ // Refresh payment
+ if (isset($_GET['refresh_payment'])) {
+ $id = (int) $_GET['refresh_payment'];
try {
- $offer = new Offer($_pdo, $id);
- if ($offer->send()) {
- $alert = "<div class='alert alert-success alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice for offer {$id} has been sent to {$offer->getContact()->email}.</div>";
- } else {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice could not be sent due to an unknown error.</div>";
- }
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The invoice could not be sent due to a PDO error.</div>";
+ $payment = new Payment($_pdo, $id);
+ $payment->refreshBraintreeStatus();
+ $alert = "<div class='alert alert-success alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The payment's status has been updated to <b>{$payment->braintree_status}</b>.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer with id {$id} could not be found.</div>";
+ $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The payment could not be refreshed due to an exception: {$e->getMessage()}.</div>";
}
}
@@ -132,10 +119,8 @@ require('./header.php');
} else {
$alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer #{$offer->id} could not be removed. Perhaps it's already removed?</div>";
}
- } catch (PDOException $e) {
- $alert = "<div class='alert alert-danger alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer could not be removed due to a PDO error.</div>";
} catch (Exception $e) {
- $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer with id {$id} could not be found.</div>";
+ $alert = "<div class='alert alert-warning alert-dismissable'><button type='button' class='close fa fa-times' data-dismiss='alert' aria-hidden='true'></button>The offer with id {$id} could not be deleted: {$e->getMessage()}.</div>";
}
}