Files
core/src/Providers/OrderServiceProvider.php
T

118 lines
5.8 KiB
PHP
Raw Normal View History

<?php
namespace Modules\Core\Providers;
use Illuminate\Console\Scheduling\Schedule as ConsoleSchedule;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
use Lunar\Models\Order;
use Lunar\Models\Transaction;
use Modules\Core\Checkout\Events\OrderPlaced;
use Modules\Core\Notification\NotificationRegistry;
use Modules\Core\Order\Commands\CloseExpiredReturnWindows;
use Modules\Core\Order\Events\OrderDelivered;
use Modules\Core\Order\Events\OrderPaidChanged;
use Modules\Core\Order\Events\OrderPickedUp;
use Modules\Core\Order\Events\OrderStatusChanged;
use Modules\Core\Order\Listeners\AdvanceFulfillmentOnCarrierCheckpoint;
use Modules\Core\Order\Listeners\AdvanceFulfillmentOnDelivered;
use Modules\Core\Order\Listeners\ApplyResolvedPaymentStatus;
use Modules\Core\Order\Listeners\CompleteOrderOnPickedUp;
use Modules\Core\Order\Listeners\DecrementStockOnOrderPlaced;
use Modules\Core\Order\Listeners\DeriveOrderDeliveredFromShipment;
use Modules\Core\Order\Listeners\MarkDeliveryFailedOnCarrierCheckpoint;
use Modules\Core\Order\Listeners\MarkOrderPlacedOnDeferredPayment;
2026-09-03 18:26:48 +03:00
use Modules\Core\Order\Listeners\RecordPaymentTransaction;
use Modules\Core\Order\Listeners\RecordStatusTransition;
use Modules\Core\Order\Models\OrderStatusTransition;
use Modules\Core\Order\Notifications\OrderCapturedNotification;
use Modules\Core\Order\Notifications\OrderCompletedNotification;
use Modules\Core\Order\Notifications\OrderDeliveredNotification;
use Modules\Core\Order\Notifications\OrderDispatchedNotification;
use Modules\Core\Order\Notifications\OrderPickupReadyNotification;
use Modules\Core\Order\Notifications\OrderPlacedNotification;
use Modules\Core\Order\Notifications\OrderRefundedNotification;
use Modules\Core\Order\Notifications\OrderStatusUpdatedNotification;
use Modules\Core\Order\Observers\OrderObserver;
use Modules\Core\Order\Observers\TransactionObserver;
use Modules\Core\Order\Support\OrderStatus;
use Modules\Core\Payment\Events\PaymentAuthorized;
use Modules\Core\Payment\Events\PaymentCaptured;
use Modules\Core\Payment\Events\PaymentDeferred;
2026-09-03 18:26:48 +03:00
use Modules\Core\Payment\Events\PaymentRefunded;
use Modules\Core\Payment\Events\PaymentVoided;
use Modules\Core\Shipping\Events\ShipmentStatusUpdatedByCarrier;
class OrderServiceProvider extends ServiceProvider
{
public function boot(): void
{
Order::observe(OrderObserver::class);
Transaction::observe(TransactionObserver::class);
Order::macro('paymentStatus', fn () => OrderStatus::payment($this));
Order::macro('fulfillmentStatus', fn () => OrderStatus::fulfillment($this));
Order::resolveRelationUsing('statusTransitions', function ($order) {
return $order->hasMany(OrderStatusTransition::class);
});
Event::listen(ShipmentStatusUpdatedByCarrier::class, DeriveOrderDeliveredFromShipment::class);
Event::listen(ShipmentStatusUpdatedByCarrier::class, AdvanceFulfillmentOnCarrierCheckpoint::class);
Event::listen(ShipmentStatusUpdatedByCarrier::class, MarkDeliveryFailedOnCarrierCheckpoint::class);
Event::listen(OrderDelivered::class, AdvanceFulfillmentOnDelivered::class);
Event::listen(OrderPickedUp::class, CompleteOrderOnPickedUp::class);
Event::listen(PaymentCaptured::class, ApplyResolvedPaymentStatus::class);
Event::listen(PaymentAuthorized::class, ApplyResolvedPaymentStatus::class);
2026-09-03 18:26:48 +03:00
Event::listen(PaymentCaptured::class, RecordPaymentTransaction::class);
Event::listen(PaymentAuthorized::class, RecordPaymentTransaction::class);
Event::listen(PaymentVoided::class, RecordPaymentTransaction::class);
// RecordPaymentTransaction must run before ApplyResolvedPaymentStatus
// for PaymentRefunded specifically — the latter now reads
// $order->transactions (via OrderStatus::payment()) to resolve
// Refunded vs PartiallyRefunded, which requires the refund
// Transaction row to already exist.
2026-09-03 18:26:48 +03:00
Event::listen(PaymentRefunded::class, RecordPaymentTransaction::class);
Event::listen(PaymentRefunded::class, ApplyResolvedPaymentStatus::class);
Event::listen(PaymentDeferred::class, MarkOrderPlacedOnDeferredPayment::class);
Event::listen(OrderPlaced::class, DecrementStockOnOrderPlaced::class);
Event::listen(OrderStatusChanged::class, [RecordStatusTransition::class, 'handleStatusChanged']);
Event::listen(OrderPaidChanged::class, [RecordStatusTransition::class, 'handlePaidChanged']);
NotificationRegistry::get()->register([
OrderDeliveredNotification::class,
OrderStatusUpdatedNotification::class,
OrderRefundedNotification::class,
OrderCapturedNotification::class,
OrderPlacedNotification::class,
OrderPickupReadyNotification::class,
OrderDispatchedNotification::class,
OrderCompletedNotification::class,
]);
// Lets the consuming app override copy/markup without forking core
// — published into resources/views/vendor/core/order/notifications,
// which loadViewsFrom() (CoreServiceProvider) already resolves
// ahead of the package's own views for the `core::` namespace.
$this->publishes([
__DIR__ . '/../../resources/views/order/notifications' => resource_path('views/vendor/core/order/notifications'),
], 'core-views');
if ($this->app->runningInConsole()) {
$this->commands([CloseExpiredReturnWindows::class]);
}
// Exact midnight per an explicit compliance requirement — not a
// loose dailyAt() offset or plain daily().
$this->app->booted(function () {
$this->app->make(ConsoleSchedule::class)
->command(CloseExpiredReturnWindows::class)
->dailyAt('00:00');
});
}
}