Skip to content

2856586 order paid in full event #792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
af3d767
2862339: Commit
haringsrob Mar 21, 2017
a055440
2862339: Test
haringsrob Mar 21, 2017
350d184
Issue #2804227 Add total_paid field and Order::getBalance() and relat…
steveoliver Sep 23, 2016
87508a3
Fix OrderInterface docblocks (thanks, phpcs).
steveoliver Sep 23, 2016
c31e6ce
Add Order tests for ::getBalance, ::getTotalPaid, ::setTotalPaid, ::a…
steveoliver Oct 17, 2016
fa1b88d
Add and subtract payments to and from order when saving and deleting …
steveoliver Feb 7, 2017
11b2b7f
Support multiple partial refunds.
steveoliver Feb 8, 2017
7593ade
Clean up Order entity test.
steveoliver Feb 8, 2017
913b912
Use Price.
steveoliver Apr 12, 2017
8f7cc93
Merge branch '8.x-2.x' into 2804227-get-order-balance
steveoliver May 25, 2017
f942120
Merge branch '2804227-get-order-balance' into 8.x-2.x
jackbravo Sep 4, 2017
14a37fd
add update to add the total_paid field
vasike Sep 4, 2017
f1678e1
Update payment states to match new names
jackbravo Sep 7, 2017
b46b571
Merge branch '8.x-2.x' into issue/2862339
jackbravo Sep 8, 2017
8dec773
Fix phpcs errors
jackbravo Sep 8, 2017
857f083
Correctly implement and test commerce_payment.load event
jackbravo Sep 8, 2017
0db777a
Watch payment status before changing order balance
jackbravo Sep 11, 2017
5395dc2
Fix two coding standard errors
jackbravo Sep 11, 2017
7051456
Merge branch 'issue/2862339' into 2856586-order-paid-in-full-event
jackbravo Sep 12, 2017
02e4555
Merge branch '8.x-2.x' into 2804227-get-order-balance-jack
jackbravo Sep 12, 2017
f8ebaf9
Rename getOrder to getEntity. It it is not returning the order.
jackbravo Sep 12, 2017
a50215b
Implement CRUD events using the event annotation on the Payment Entity
jackbravo Sep 12, 2017
df566da
Rename PaymentEvent::getOrder to getEntity because it returns the pay…
jackbravo Sep 12, 2017
6c56ec6
Implement CRUD events using the event annotation on the Payment Entity
jackbravo Sep 12, 2017
4dd5cf3
Test also the getEntity method inside PaymentEvent
jackbravo Sep 12, 2017
5343c0e
Test also the getEntity method inside PaymentEvent
jackbravo Sep 12, 2017
ce54df0
Issue #2856586 by haringsrob, jackbravo: extract event dispatching to…
jackbravo Sep 12, 2017
4f4bc64
Fix coding standard errors
jackbravo Sep 12, 2017
572f8e8
Merge branch 'issue/2862339' into 2856586-order-paid-in-full-event
jackbravo Sep 12, 2017
15ee868
Dispatch PAYMENT_ORDER_PAID_IN_FULL event when balance isZero on Paym…
jackbravo Sep 12, 2017
799f07f
Merge remote-tracking branch 'commerce/8.x-2.x' into 2804227-get-orde…
jackbravo Sep 15, 2017
91c03f0
remove the word 'remaining' -- it seems redundant
steveoliver Sep 15, 2017
4eb3cce
Merge branch '2804227-get-order-balance-jack' into 2856586-order-paid…
jackbravo Sep 15, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion modules/order/commerce_order.post_update.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Post update functions for Order.
*/

use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\Entity\EntityFormDisplay;

/**
Expand Down Expand Up @@ -158,7 +159,8 @@ function commerce_order_post_update_5() {
function commerce_order_post_update_6() {
// Remove the default_country setting from any profile form.
// That allows Commerce to apply its own default taken from the store.
$query = \Drupal::entityQuery('entity_form_display')->condition('targetEntityType', 'profile');
$query = \Drupal::entityQuery('entity_form_display')
->condition('targetEntityType', 'profile');
$ids = $query->execute();
$form_displays = EntityFormDisplay::loadMultiple($ids);
foreach ($form_displays as $id => $form_display) {
Expand All @@ -170,3 +172,18 @@ function commerce_order_post_update_6() {
}
}
}

/**
* Add 'total_paid' field to 'commerce_order' entities.
*/
function commerce_order_post_update_7() {
$storage_definition = BaseFieldDefinition::create('commerce_price')
->setLabel(t('Total paid'))
->setDescription(t('The total amount paid on the order.'))
->setReadOnly(TRUE)
->setDisplayConfigurable('form', FALSE)
->setDisplayConfigurable('view', TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('total_paid', 'commerce_order', 'commerce_order', $storage_definition);
return t('The order total paid field was created.');
}
51 changes: 51 additions & 0 deletions modules/order/src/Entity/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Drupal\commerce_order\Entity;

use Drupal\commerce_order\Adjustment;
use Drupal\commerce_price\Price;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
Expand Down Expand Up @@ -385,6 +386,49 @@ public function getTotalPrice() {
}
}

/**
* {@inheritdoc}
*/
public function addPayment(Price $amount) {
$this->setTotalPaid($this->getTotalPaid()->add($amount));
return $this;
}

/**
* {@inheritdoc}
*/
public function subtractPayment(Price $amount) {
$this->setTotalPaid($this->getTotalPaid()->subtract($amount));
return $this;
}

/**
* {@inheritdoc}
*/
public function getTotalPaid() {
if (!$this->get('total_paid')->isEmpty()) {
return $this->get('total_paid')->first()->toPrice();
}
return new Price('0', $this->getStore()->getDefaultCurrencyCode());
}

/**
* {@inheritdoc}
*/
public function setTotalPaid(Price $amount) {
$this->set('total_paid', $amount);
}

/**
* {@inheritdoc}
*/
public function getBalance() {
if ($this->getTotalPrice() && $this->getTotalPaid()) {
return $this->getTotalPrice()->subtract($this->getTotalPaid());
}
return $this->getTotalPrice();
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -643,6 +687,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setDisplayConfigurable('form', FALSE)
->setDisplayConfigurable('view', TRUE);

$fields['total_paid'] = BaseFieldDefinition::create('commerce_price')
->setLabel(t('Total paid'))
->setDescription(t('The total amount paid on the order.'))
->setReadOnly(TRUE)
->setDisplayConfigurable('form', FALSE)
->setDisplayConfigurable('view', TRUE);

$fields['state'] = BaseFieldDefinition::create('state')
->setLabel(t('State'))
->setDescription(t('The order state.'))
Expand Down
45 changes: 45 additions & 0 deletions modules/order/src/Entity/OrderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Drupal\commerce_order\Entity;

use Drupal\commerce_order\EntityAdjustableInterface;
use Drupal\commerce_price\Price;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
Expand Down Expand Up @@ -272,6 +273,50 @@ public function recalculateTotalPrice();
*/
public function getTotalPrice();

/**
* Adds an amount to the order total paid.
*
* @param \Drupal\commerce_price\Price $amount
* The amount to add to the total paid.
*
* @return $this
*/
public function addPayment(Price $amount);

/**
* Subtracts an amount from the order total paid.
*
* @param \Drupal\commerce_price\Price $amount
* The amount to subtract from the total paid.
*
* @return $this
*/
public function subtractPayment(Price $amount);

/**
* Gets the total amount paid on the order.
*
* @return \Drupal\commerce_price\Price
* The order total paid amount.
*/
public function getTotalPaid();

/**
* Sets the total amount paid on the order.
*
* @param \Drupal\commerce_price\Price $amount
* The amount to set as the order total paid.
*/
public function setTotalPaid(Price $amount);

/**
* Gets the amount unpaid on the order.
*
* @return \Drupal\commerce_price\Price|null
* The total order amount minus the total paid, or NULL.
*/
public function getBalance();

/**
* Gets the order state.
*
Expand Down
93 changes: 92 additions & 1 deletion modules/order/tests/src/Kernel/Entity/OrderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Drupal\commerce_order\Entity\OrderItemType;
use Drupal\commerce_price\Exception\CurrencyMismatchException;
use Drupal\commerce_price\Price;
use Drupal\commerce_payment\Entity\Payment;
use Drupal\commerce_payment\Entity\PaymentGateway;
use Drupal\profile\Entity\Profile;
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;

Expand All @@ -27,6 +29,13 @@ class OrderTest extends CommerceKernelTestBase {
*/
protected $user;

/**
* The payment gateway plugin.
*
* @var \Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsRefundsInterface
*/
protected $payment_gateway_plugin;

/**
* Modules to enable.
*
Expand All @@ -36,6 +45,8 @@ class OrderTest extends CommerceKernelTestBase {
'entity_reference_revisions',
'profile',
'state_machine',
'commerce_payment',
'commerce_payment_example',
'commerce_product',
'commerce_order',
];
Expand All @@ -49,6 +60,7 @@ protected function setUp() {
$this->installEntitySchema('profile');
$this->installEntitySchema('commerce_order');
$this->installEntitySchema('commerce_order_item');
$this->installEntitySchema('commerce_payment');
$this->installConfig('commerce_order');

// An order item type that doesn't need a purchasable entity, for simplicity.
Expand All @@ -58,6 +70,14 @@ protected function setUp() {
'orderType' => 'default',
])->save();

$payment_gateway = PaymentGateway::create([
'id' => 'example',
'label' => 'Example',
'plugin' => 'example_onsite',
]);
$payment_gateway->save();
$this->payment_gateway_plugin = $payment_gateway->getPlugin();

$user = $this->createUser();
$this->user = $this->reloadEntity($user);
}
Expand Down Expand Up @@ -94,6 +114,11 @@ protected function setUp() {
* @covers ::getSubtotalPrice
* @covers ::recalculateTotalPrice
* @covers ::getTotalPrice
* @covers ::getBalance
* @covers ::addPayment
* @covers ::subtractPayment
* @covers ::setTotalPaid
* @covers ::getTotalPaid
* @covers ::getState
* @covers ::getRefreshState
* @covers ::setRefreshState
Expand Down Expand Up @@ -133,6 +158,7 @@ public function testOrder() {
$order = Order::create([
'type' => 'default',
'state' => 'completed',
'store_id' => $this->store->id(),
]);
$order->save();

Expand Down Expand Up @@ -178,6 +204,7 @@ public function testOrder() {
$this->assertNotEmpty($order->hasItem($another_order_item));

$this->assertEquals(new Price('8.00', 'USD'), $order->getTotalPrice());
$this->assertEquals(new Price('8.00', 'USD'), $order->getBalance());
$adjustments = [];
$adjustments[] = new Adjustment([
'type' => 'custom',
Expand Down Expand Up @@ -208,6 +235,7 @@ public function testOrder() {
$order->removeAdjustment($adjustments[0]);
$this->assertEquals(new Price('8.00', 'USD'), $order->getSubtotalPrice());
$this->assertEquals(new Price('18.00', 'USD'), $order->getTotalPrice());
$this->assertEquals(new Price('18.00', 'USD'), $order->getBalance());
$this->assertEquals([$adjustments[1], $adjustments[2]], $order->getAdjustments());
$order->setAdjustments($adjustments);
$this->assertEquals($adjustments, $order->getAdjustments());
Expand All @@ -221,9 +249,72 @@ public function testOrder() {
'amount' => new Price('5.00', 'USD'),
]));
$order->addItem($another_order_item);
$this->assertEquals(new Price('27.00', 'USD'), $order->getTotalPrice());
$collected_adjustments = $order->collectAdjustments();
$this->assertEquals(new Price('10.00', 'USD'), $collected_adjustments[2]->getAmount());
$this->assertEquals(new Price('27.00', 'USD'), $order->getTotalPrice());
$this->assertEquals(new Price('27.00', 'USD'), $order->getBalance());

// Test that payments update the order total paid and balance.
$order->save();
$payment = Payment::create([
'order_id' => $order->id(),
'amount' => new Price('25.00', 'USD'),
'payment_gateway' => 'example',
'state' => 'completed',
]);
$payment->save();
$order = Order::load($order->id());
$this->assertEquals(new Price('2.00', 'USD'), $order->getBalance());
$this->payment_gateway_plugin->refundPayment($payment, new Price('5.00', 'USD'));
$order = Order::load($order->id());
$this->assertEquals(new Price('7.00', 'USD'), $order->getBalance());
$payment->delete();
$order = Order::load($order->id());
$this->assertEquals(new Price('27.00', 'USD'), $order->getBalance());
$payment2 = Payment::create([
'order_id' => $order->id(),
'amount' => new Price('27.00', 'USD'),
'payment_gateway' => 'example',
'state' => 'completed',
]);
$payment2->save();
$order = Order::load($order->id());
$this->assertEquals(new Price('0.00', 'USD'), $order->getBalance());

// Test that payments can be partially refunded multiple times.
$this->payment_gateway_plugin->refundPayment($payment2, new Price('17.00', 'USD'));
$order = Order::load($order->id());
$this->assertEquals(new Price('17.00', 'USD'), $order->getBalance());
$this->payment_gateway_plugin->refundPayment($payment2, new Price('5.00', 'USD'));
$order = Order::load($order->id());
$this->assertEquals(new Price('22.00', 'USD'), $order->getBalance());

// Test that the total paid amount can be set explicitly on the order.
$order->setTotalPaid(new Price('0.00', 'USD'));
$order->save();
$this->assertEquals(new Price('27.00', 'USD'), $order->getBalance());

// Test that payments only substract total when setting to completed.
$order->save();
$payment = Payment::create([
'order_id' => $order->id(),
'amount' => new Price('25.00', 'USD'),
'payment_gateway' => 'example',
]);
$payment->save();
$order = Order::load($order->id());
$this->assertEquals(new Price('0.00', 'USD'), $order->getTotalPaid());

$payment->setState('completed');
$payment->save();
$order = Order::load($order->id());
$this->assertEquals(new Price('25.00', 'USD'), $order->getTotalPaid());
$this->assertEquals(new Price('2.00', 'USD'), $order->getBalance());

// Test that deleted payments update the order total paid and balance.
$payment->delete();
$order = Order::load($order->id());
$this->assertEquals(new Price('0.00', 'USD'), $order->getTotalPaid());

$this->assertEquals('completed', $order->getState()->value);

Expand Down
30 changes: 29 additions & 1 deletion modules/payment/src/Entity/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Drupal\commerce_payment\Entity;

use Drupal\commerce_payment\Event\PaymentEvents;
use Drupal\commerce_payment\PaymentStorage;
use Drupal\commerce_price\Price;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityMalformedException;
Expand All @@ -25,6 +27,7 @@
* bundle_label = @Translation("Payment type"),
* bundle_plugin_type = "commerce_payment_type",
* handlers = {
* "event" = "Drupal\commerce_payment\Event\PaymentEvent",
* "access" = "Drupal\commerce_payment\PaymentAccessControlHandler",
* "list_builder" = "Drupal\commerce_payment\PaymentListBuilder",
* "storage" = "Drupal\commerce_payment\PaymentStorage",
Expand Down Expand Up @@ -290,7 +293,8 @@ public function preSave(EntityStorageInterface $storage) {
$refunded_amount = new Price('0', $this->getAmount()->getCurrencyCode());
$this->setRefundedAmount($refunded_amount);
}
// Maintain the authorized completed timestamps.
// Maintain the authorized completed timestamps while also maintaining the
// order balance.
$state = $this->getState()->value;
$original_state = isset($this->original) ? $this->original->getState()->value : '';
if ($state == 'authorized' && $original_state != 'authorized') {
Expand All @@ -299,9 +303,33 @@ public function preSave(EntityStorageInterface $storage) {
}
}
if ($state == 'completed' && $original_state != 'completed') {
$this->getOrder()->addPayment($this->getAmount())->save();
if (empty($this->getCompletedTime())) {
$this->setCompletedTime(\Drupal::time()->getRequestTime());
}
if ($this->getOrder()->getBalance()->isZero() && $storage instanceof PaymentStorage) {
$storage->dispatchPaymentEvent($this, PaymentEvents::PAYMENT_ORDER_PAID_IN_FULL);
}
}
elseif (in_array($state, ['partially_refunded', 'refunded']) &&
in_array($original_state, ['completed', 'partially_refunded'])) {
$original = $this->values['original'];
$net_refund = $this->getRefundedAmount()->subtract($original->getRefundedAmount());
$this->getOrder()->subtractPayment($net_refund)->save();
}
}

/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageInterface $storage, array $entities) {
parent::preDelete($storage, $entities);

// Subtract each payment from order.
foreach ($entities as $payment) {
$net_payment = $payment->getAmount()
->subtract($payment->getRefundedAmount());
$payment->getOrder()->subtractPayment($net_payment)->save();
}
}

Expand Down
Loading