diff --git a/Api/Config/RepositoryInterface.php b/Api/Config/RepositoryInterface.php index 1938b9f..44a21b8 100755 --- a/Api/Config/RepositoryInterface.php +++ b/Api/Config/RepositoryInterface.php @@ -12,6 +12,12 @@ interface RepositoryInterface extends System\DataInterface public const XML_PATH_EXTENSION_VERSION = 'faslet_connect/general/version'; public const XML_PATH_EXTENSION_ENABLE = 'faslet_connect/general/enable'; public const XML_PATH_DEBUG = 'faslet_connect/general/debug'; + public const XML_PATH_RMA_EXPORT_ENABLE = 'faslet_connect/rma/export_enabled'; + public const XML_PATH_RMA_ENDPOINT_URL = 'faslet_connect/rma/endpoint_url'; + public const XML_PATH_RMA_AUTH_TOKEN = 'faslet_connect/rma/auth_token'; + public const XML_PATH_RMA_TIMEOUT = 'faslet_connect/rma/timeout'; + public const XML_PATH_RMA_LIFECYCLE_EVENTS = 'faslet_connect/rma/lifecycle_events'; + public const DEFAULT_RMA_TIMEOUT = 5; public const MODULE_SUPPORT_LINK = 'https://faslet.me/faslet/contact'; /** @@ -59,4 +65,59 @@ public function getSupportLink(): string; * @return string */ public function getStoreUrl(): string; + + /** + * Check if Commerce RMA export is enabled. + * + * @param int|null $storeId + * + * @return bool + */ + public function isRmaExportEnabled(?int $storeId = null): bool; + + /** + * Get the Faslet RMA ingestion endpoint. + * + * @param int|null $storeId + * + * @return string|null + */ + public function getRmaEndpointUrl(?int $storeId = null): ?string; + + /** + * Get the Faslet auth token used for RMA exports. + * + * @param int|null $storeId + * + * @return string|null + */ + public function getRmaAuthToken(?int $storeId = null): ?string; + + /** + * Get the outbound request timeout for RMA exports. + * + * @param int|null $storeId + * + * @return int + */ + public function getRmaTimeout(?int $storeId = null): int; + + /** + * Get the configured RMA lifecycle events that should be exported. + * + * @param int|null $storeId + * + * @return array + */ + public function getRmaLifecycleEvents(?int $storeId = null): array; + + /** + * Check if a specific lifecycle event should be exported. + * + * @param string $eventCode + * @param int|null $storeId + * + * @return bool + */ + public function shouldExportRmaLifecycleEvent(string $eventCode, ?int $storeId = null): bool; } diff --git a/Api/Config/System/DataInterface.php b/Api/Config/System/DataInterface.php index ccceeac..e5f67b1 100644 --- a/Api/Config/System/DataInterface.php +++ b/Api/Config/System/DataInterface.php @@ -18,7 +18,7 @@ interface DataInterface /** * @return string|null */ - public function getShopId(): ?string; + public function getShopId(?int $storeId = null): ?string; /** * @return array diff --git a/Model/Config/Repository.php b/Model/Config/Repository.php index 6e1065d..f28120f 100755 --- a/Model/Config/Repository.php +++ b/Model/Config/Repository.php @@ -57,4 +57,59 @@ public function getStoreUrl(): string { return $this->getStore()->getBaseUrl(); } + + /** + * @inheritDoc + */ + public function isRmaExportEnabled(?int $storeId = null): bool + { + return $this->isSetFlag(self::XML_PATH_RMA_EXPORT_ENABLE, $storeId); + } + + /** + * @inheritDoc + */ + public function getRmaEndpointUrl(?int $storeId = null): ?string + { + return $this->getStoreValue(self::XML_PATH_RMA_ENDPOINT_URL, $storeId); + } + + /** + * @inheritDoc + */ + public function getRmaAuthToken(?int $storeId = null): ?string + { + return $this->getDecryptedStoreValue(self::XML_PATH_RMA_AUTH_TOKEN, $storeId); + } + + /** + * @inheritDoc + */ + public function getRmaTimeout(?int $storeId = null): int + { + $timeout = (int)$this->getStoreValue(self::XML_PATH_RMA_TIMEOUT, $storeId); + + return $timeout > 0 ? $timeout : self::DEFAULT_RMA_TIMEOUT; + } + + /** + * @inheritDoc + */ + public function getRmaLifecycleEvents(?int $storeId = null): array + { + $rawValue = (string)$this->getStoreValue(self::XML_PATH_RMA_LIFECYCLE_EVENTS, $storeId); + if ($rawValue === '') { + return []; + } + + return array_values(array_filter(array_map('trim', explode(',', $rawValue)))); + } + + /** + * @inheritDoc + */ + public function shouldExportRmaLifecycleEvent(string $eventCode, ?int $storeId = null): bool + { + return in_array($eventCode, $this->getRmaLifecycleEvents($storeId), true); + } } diff --git a/Model/Config/System/BaseRepository.php b/Model/Config/System/BaseRepository.php index 7633043..4ad1a70 100644 --- a/Model/Config/System/BaseRepository.php +++ b/Model/Config/System/BaseRepository.php @@ -4,6 +4,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ProductMetadata; +use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Serialize\Serializer\Json; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\ScopeInterface; @@ -28,6 +29,10 @@ class BaseRepository * @var StoreManagerInterface */ protected $storeManager; + /** + * @var EncryptorInterface + */ + protected $encryptor; /** * BaseRepository constructor. @@ -35,17 +40,20 @@ class BaseRepository * @param Json $json * @param ProductMetadata $metadata * @param StoreManagerInterface $storeManager + * @param EncryptorInterface $encryptor */ public function __construct( ScopeConfigInterface $scopeConfig, Json $json, ProductMetadata $metadata, - StoreManagerInterface $storeManager + StoreManagerInterface $storeManager, + EncryptorInterface $encryptor ) { $this->scopeConfig = $scopeConfig; $this->json = $json; $this->metadata = $metadata; $this->storeManager = $storeManager; + $this->encryptor = $encryptor; } /** @@ -82,6 +90,26 @@ protected function isSetFlag(string $path, ?int $storeId = null, ?string $scope return $this->scopeConfig->isSetFlag($path, $scope, $storeId); } + /** + * Retrieve and decrypt a config value by path, storeId and scope. + * + * @param string $path + * @param int|null $storeId + * @param string|null $scope + * + * @return string|null + */ + protected function getDecryptedStoreValue(string $path, ?int $storeId = null, ?string $scope = null): ?string + { + $value = $this->getStoreValue($path, $storeId, $scope); + + if ($value === null || $value === '') { + return $value; + } + + return $this->encryptor->decrypt($value); + } + /** * @return StoreInterface */ diff --git a/Model/Config/System/DataRepository.php b/Model/Config/System/DataRepository.php index 9495629..face180 100644 --- a/Model/Config/System/DataRepository.php +++ b/Model/Config/System/DataRepository.php @@ -10,9 +10,9 @@ class DataRepository extends BaseRepository implements DataInterface /** * @inheritDoc */ - public function getShopId(): ?string + public function getShopId(?int $storeId = null): ?string { - return $this->getStoreValue(self::SHOP_ID); + return $this->getStoreValue(self::SHOP_ID, $storeId); } /** diff --git a/Model/Rma/ExportService.php b/Model/Rma/ExportService.php new file mode 100644 index 0000000..cb4e9c5 --- /dev/null +++ b/Model/Rma/ExportService.php @@ -0,0 +1,252 @@ +configRepository = $configRepository; + $this->logRepository = $logRepository; + $this->moduleManager = $moduleManager; + $this->orderRepository = $orderRepository; + $this->payloadBuilder = $payloadBuilder; + $this->webhookPublisher = $webhookPublisher; + } + + /** + * Export an Adobe Commerce RMA model to the configured Faslet backend. + * + * @param object $rma + * + * @return void + */ + public function export(object $rma): void + { + if (!$this->moduleManager->isEnabled('Magento_Rma') || !$this->isRmaEntity($rma)) { + return; + } + + $orderId = (int)$this->readValue($rma, ['order_id']); + if ($orderId <= 0) { + $this->logRepository->addErrorLog('rma_export_missing_order', $this->extractRawData($rma)); + return; + } + + try { + $order = $this->orderRepository->get($orderId); + } catch (\Exception $exception) { + $this->logRepository->addErrorLog('rma_export_order_lookup_failed', [ + 'order_id' => $orderId, + 'message' => $exception->getMessage(), + ]); + return; + } + + $storeId = (int)$order->getStoreId(); + if (!$this->configRepository->isEnabled($storeId) || !$this->configRepository->isRmaExportEnabled($storeId)) { + return; + } + + if (!$this->configRepository->getRmaEndpointUrl($storeId)) { + return; + } + + $eventCode = $this->resolveLifecycleEvent($rma, $storeId); + if ($eventCode === null) { + return; + } + + $payload = $this->payloadBuilder->build($rma, $order, $eventCode); + $this->logRepository->addDebugLog('rma_export_payload', $payload); + $this->webhookPublisher->publish($payload, $storeId); + } + + /** + * @param object $rma + * + * @return bool + */ + private function isRmaEntity(object $rma): bool + { + return is_a($rma, self::RMA_MODEL_CLASS); + } + + /** + * @param object $rma + * @param int $storeId + * + * @return string|null + */ + private function resolveLifecycleEvent(object $rma, int $storeId): ?string + { + $status = $this->normalizeStatus((string)$this->readValue($rma, ['status'])); + $previousStatus = $this->normalizeStatus((string)$this->readOrigValue($rma, ['status'])); + + if ($this->isCreated($rma) && $this->configRepository->shouldExportRmaLifecycleEvent('created', $storeId)) { + return 'created'; + } + + if ($status && $status !== $previousStatus && $this->configRepository->shouldExportRmaLifecycleEvent($status, $storeId)) { + return $status; + } + + return null; + } + + /** + * @param object $rma + * + * @return bool + */ + private function isCreated(object $rma): bool + { + if (method_exists($rma, 'isObjectNew') && $rma->isObjectNew()) { + return true; + } + + $originalEntityId = $this->readOrigValue($rma, ['entity_id', 'id']); + $originalIncrementId = $this->readOrigValue($rma, ['increment_id']); + + if ($originalEntityId === null && $originalIncrementId === null) { + $createdAt = (string)$this->readValue($rma, ['created_at', 'date_requested']); + $updatedAt = (string)$this->readValue($rma, ['updated_at']); + + if ($createdAt !== '' && $createdAt === $updatedAt) { + return true; + } + } + + return $originalEntityId === null && $originalIncrementId === null; + } + + /** + * @param object $subject + * @param array $keys + * + * @return mixed|null + */ + private function readValue(object $subject, array $keys) + { + foreach ($keys as $key) { + $camelizedKey = str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + $getter = 'get' . $camelizedKey; + + if (method_exists($subject, $getter)) { + $value = $subject->{$getter}(); + if ($value !== null && $value !== '') { + return $value; + } + } + + if ($subject instanceof DataObject) { + $value = $subject->getData($key); + if ($value !== null && $value !== '') { + return $value; + } + } + } + + return null; + } + + /** + * @param object $subject + * @param array $keys + * + * @return mixed|null + */ + private function readOrigValue(object $subject, array $keys) + { + if (!method_exists($subject, 'getOrigData')) { + return null; + } + + foreach ($keys as $key) { + $value = $subject->getOrigData($key); + if ($value !== null && $value !== '') { + return $value; + } + } + + return null; + } + + /** + * @param object $subject + * + * @return array + */ + private function extractRawData(object $subject): array + { + if ($subject instanceof DataObject) { + return $subject->getData(); + } + + if (method_exists($subject, 'getData')) { + $data = $subject->getData(); + if (is_array($data)) { + return $data; + } + } + + return []; + } + + /** + * @param string $status + * + * @return string|null + */ + private function normalizeStatus(string $status): ?string + { + $status = trim($status); + if ($status === '') { + return null; + } + + $normalized = strtolower((string)preg_replace('/[^a-z0-9]+/i', '_', $status)); + + return trim($normalized, '_'); + } +} \ No newline at end of file diff --git a/Model/Rma/PayloadBuilder.php b/Model/Rma/PayloadBuilder.php new file mode 100644 index 0000000..e97dca6 --- /dev/null +++ b/Model/Rma/PayloadBuilder.php @@ -0,0 +1,353 @@ +configRepository = $configRepository; + $this->productRepository = $productRepository; + } + + /** + * Build a backend ingestion payload for an Adobe Commerce RMA export. + * + * @param object $rma + * @param OrderInterface $order + * @param string $eventCode + * + * @return array + */ + public function build(object $rma, OrderInterface $order, string $eventCode): array + { + $storeId = (int)$order->getStoreId(); + $status = $this->normalizeStatus((string)$this->readValue($rma, ['status'])); + $previousStatus = $this->normalizeStatus((string)$this->readOrigValue($rma, ['status'])); + + return [ + 'event' => $eventCode, + 'event_id' => $this->buildEventId($rma, $eventCode), + 'occurred_at' => $this->readValue($rma, ['updated_at', 'date_requested', 'created_at']), + 'shop_id' => $this->configRepository->getShopId($storeId), + 'store' => [ + 'id' => $storeId, + 'code' => $order->getStoreName(), + ], + 'order' => [ + 'entity_id' => (int)$order->getEntityId(), + 'increment_id' => $order->getIncrementId(), + 'customer_email' => $order->getCustomerEmail(), + ], + 'rma' => [ + 'entity_id' => $this->readValue($rma, ['entity_id', 'id']), + 'increment_id' => $this->readValue($rma, ['increment_id']), + 'status' => $status, + 'previous_status' => $previousStatus, + 'created_at' => $this->readValue($rma, ['created_at', 'date_requested']), + 'updated_at' => $this->readValue($rma, ['updated_at']), + 'comments' => $this->extractComments($rma), + 'attributes' => $this->sanitizeData($this->extractRawData($rma)), + ], + 'items' => $this->buildItems($rma, $order), + ]; + } + + /** + * @param object $rma + * @param OrderInterface $order + * + * @return array + */ + private function buildItems(object $rma, OrderInterface $order): array + { + $items = []; + $storeId = (int)$order->getStoreId(); + $orderItems = $this->indexOrderItems($order); + + foreach ($this->extractItems($rma) as $rmaItem) { + $orderItemId = (int)$this->readValue($rmaItem, ['order_item_id']); + $orderItem = $orderItems[$orderItemId] ?? null; + $productData = $this->extractProductData($orderItem, $storeId); + + $items[] = [ + 'entity_id' => $this->readValue($rmaItem, ['entity_id', 'id']), + 'order_item_id' => $orderItemId ?: null, + 'product_id' => $this->readValue($rmaItem, ['product_id']) ?: ($orderItem ? (int)$orderItem->getProductId() : null), + 'sku' => $this->readValue($rmaItem, ['product_sku', 'sku']) ?: ($orderItem ? $orderItem->getSku() : null), + 'title' => $this->readValue($rmaItem, ['product_name', 'name']) ?: ($orderItem ? $orderItem->getName() : null), + 'correlation_id' => $productData['correlation_id'], + 'variant_id' => $productData['variant_id'], + 'requested_qty' => $this->readValue($rmaItem, ['qty_requested', 'qty']), + 'authorized_qty' => $this->readValue($rmaItem, ['qty_authorized']), + 'returned_qty' => $this->readValue($rmaItem, ['qty_returned']), + 'reason' => $this->readValue($rmaItem, ['reason']), + 'reason_label' => $this->readValue($rmaItem, ['reason_label', 'reason']), + 'condition' => $this->readValue($rmaItem, ['condition']), + 'condition_label' => $this->readValue($rmaItem, ['condition_label', 'condition']), + 'resolution' => $this->readValue($rmaItem, ['resolution']), + 'resolution_label' => $this->readValue($rmaItem, ['resolution_label', 'resolution']), + 'attributes' => $this->sanitizeData($this->extractRawData($rmaItem)), + ]; + } + + return $items; + } + + /** + * @param OrderInterface $order + * + * @return array + */ + private function indexOrderItems(OrderInterface $order): array + { + $items = []; + + foreach ($order->getItems() as $orderItem) { + $items[(int)$orderItem->getItemId()] = $orderItem; + } + + return $items; + } + + /** + * @param OrderItemInterface|null $orderItem + * @param int $storeId + * + * @return array + */ + private function extractProductData(?OrderItemInterface $orderItem, int $storeId): array + { + if ($orderItem === null) { + return [ + 'correlation_id' => null, + 'variant_id' => null, + ]; + } + + $product = $orderItem->getProduct(); + if ($product === null && $orderItem->getProductId()) { + try { + $product = $this->productRepository->getById((int)$orderItem->getProductId(), false, $storeId); + } catch (\Exception $exception) { + $product = null; + } + } + + if ($product === null) { + return [ + 'correlation_id' => null, + 'variant_id' => null, + ]; + } + + $attributes = $this->configRepository->getAttributes($storeId); + $identifierAttribute = $attributes['identifier'] ?? null; + + return [ + 'correlation_id' => $identifierAttribute ? $product->getData($identifierAttribute) : null, + 'variant_id' => $identifierAttribute ? $product->getData($identifierAttribute) : null, + ]; + } + + /** + * @param object $rma + * + * @return iterable + */ + private function extractItems(object $rma): iterable + { + foreach (['getItems', 'getItemsCollection'] as $method) { + if (method_exists($rma, $method)) { + $items = $rma->{$method}(); + if (is_array($items) || $items instanceof \Traversable) { + return $items; + } + } + } + + $items = $this->readValue($rma, ['items']); + if (is_array($items) || $items instanceof \Traversable) { + return $items; + } + + return []; + } + + /** + * @param object $rma + * + * @return array + */ + private function extractComments(object $rma): array + { + $result = []; + + foreach (['getComments', 'getCommentsCollection'] as $method) { + if (!method_exists($rma, $method)) { + continue; + } + + $comments = $rma->{$method}(); + if (!is_array($comments) && !$comments instanceof \Traversable) { + continue; + } + + foreach ($comments as $comment) { + $result[] = [ + 'entity_id' => $this->readValue($comment, ['entity_id', 'id']), + 'comment' => $this->readValue($comment, ['comment', 'comment_text']), + 'created_at' => $this->readValue($comment, ['created_at']), + 'status' => $this->readValue($comment, ['status']), + ]; + } + + if ($result) { + return $result; + } + } + + return $result; + } + + /** + * @param object $subject + * @param array $keys + * + * @return mixed|null + */ + private function readValue(object $subject, array $keys) + { + foreach ($keys as $key) { + $camelizedKey = str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + $getter = 'get' . $camelizedKey; + + if (method_exists($subject, $getter)) { + $value = $subject->{$getter}(); + if ($value !== null && $value !== '') { + return $value; + } + } + + if ($subject instanceof DataObject) { + $value = $subject->getData($key); + if ($value !== null && $value !== '') { + return $value; + } + } + } + + return null; + } + + /** + * @param object $subject + * @param array $keys + * + * @return mixed|null + */ + private function readOrigValue(object $subject, array $keys) + { + if (!method_exists($subject, 'getOrigData')) { + return null; + } + + foreach ($keys as $key) { + $value = $subject->getOrigData($key); + if ($value !== null && $value !== '') { + return $value; + } + } + + return null; + } + + /** + * @param object $subject + * + * @return array + */ + private function extractRawData(object $subject): array + { + if ($subject instanceof DataObject) { + return $subject->getData(); + } + + if (method_exists($subject, 'getData')) { + $data = $subject->getData(); + if (is_array($data)) { + return $data; + } + } + + return []; + } + + /** + * @param array $data + * + * @return array + */ + private function sanitizeData(array $data): array + { + foreach ($data as $key => $value) { + if (is_object($value)) { + unset($data[$key]); + continue; + } + + if (is_array($value)) { + $data[$key] = $this->sanitizeData($value); + } + } + + return $data; + } + + /** + * @param object $rma + * @param string $eventCode + * + * @return string + */ + private function buildEventId(object $rma, string $eventCode): string + { + $rmaId = (string)$this->readValue($rma, ['increment_id', 'entity_id', 'id']); + + return sprintf('faslet_rma:%s:%s', $rmaId, $eventCode); + } + + /** + * @param string $status + * + * @return string|null + */ + private function normalizeStatus(string $status): ?string + { + $status = trim($status); + if ($status === '') { + return null; + } + + $normalized = strtolower((string)preg_replace('/[^a-z0-9]+/i', '_', $status)); + + return trim($normalized, '_'); + } +} \ No newline at end of file diff --git a/Model/Rma/WebhookPublisher.php b/Model/Rma/WebhookPublisher.php new file mode 100644 index 0000000..4c2d775 --- /dev/null +++ b/Model/Rma/WebhookPublisher.php @@ -0,0 +1,97 @@ +configRepository = $configRepository; + $this->curlFactory = $curlFactory; + $this->json = $json; + $this->logRepository = $logRepository; + } + + /** + * Publish the given payload to the configured Faslet endpoint. + * + * @param array $payload + * @param int $storeId + * + * @return void + */ + public function publish(array $payload, int $storeId): void + { + $url = $this->configRepository->getRmaEndpointUrl($storeId); + if (!$url) { + return; + } + + $requestBody = $this->json->serialize($payload); + $eventId = (string)($payload['event_id'] ?? 'unknown'); + + try { + $curl = $this->curlFactory->create(); + $timeout = $this->configRepository->getRmaTimeout($storeId); + $curl->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); + $curl->setOption(CURLOPT_TIMEOUT, $timeout); + $curl->addHeader('Accept', 'application/json'); + $curl->addHeader('Content-Type', 'application/json'); + $curl->addHeader('X-Faslet-Event-Id', $eventId); + + if ($token = $this->configRepository->getRmaAuthToken($storeId)) { + $curl->addHeader('Authorization', 'Bearer ' . $token); + } + + $curl->post($url, $requestBody); + + $logPayload = [ + 'url' => $url, + 'store_id' => $storeId, + 'status' => $curl->getStatus(), + 'body' => $curl->getBody(), + 'event_id' => $eventId, + ]; + + if ($curl->getStatus() >= 400) { + $this->logRepository->addErrorLog('rma_export_response', $logPayload); + return; + } + + $this->logRepository->addDebugLog('rma_export_response', $logPayload); + } catch (\Exception $exception) { + $this->logRepository->addErrorLog('rma_export_exception', [ + 'store_id' => $storeId, + 'event_id' => $eventId, + 'message' => $exception->getMessage(), + ]); + } + } +} \ No newline at end of file diff --git a/Model/System/Config/Source/RmaLifecycleEvent.php b/Model/System/Config/Source/RmaLifecycleEvent.php new file mode 100644 index 0000000..90a696c --- /dev/null +++ b/Model/System/Config/Source/RmaLifecycleEvent.php @@ -0,0 +1,27 @@ + 'created', 'label' => __('RMA Created / Requested')], + ['value' => 'authorized', 'label' => __('Authorized')], + ['value' => 'partially_authorized', 'label' => __('Partially Authorized')], + ['value' => 'return_received', 'label' => __('Return Received')], + ['value' => 'return_partially_received', 'label' => __('Return Partially Received')], + ['value' => 'approved', 'label' => __('Approved')], + ['value' => 'rejected', 'label' => __('Rejected')], + ['value' => 'processed_and_closed', 'label' => __('Processed and Closed')], + ['value' => 'closed', 'label' => __('Closed')], + ]; + } +} \ No newline at end of file diff --git a/Observer/ExportRmaAfterSave.php b/Observer/ExportRmaAfterSave.php new file mode 100644 index 0000000..d154051 --- /dev/null +++ b/Observer/ExportRmaAfterSave.php @@ -0,0 +1,46 @@ +exportService = $exportService; + $this->moduleManager = $moduleManager; + } + + /** + * @inheritDoc + */ + public function execute(Observer $observer): void + { + if (!$this->moduleManager->isEnabled('Magento_Rma')) { + return; + } + + $object = $observer->getEvent()->getObject(); + if (!is_object($object)) { + return; + } + + $this->exportService->export($object); + } +} \ No newline at end of file diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 713439e..c03191f 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -11,6 +11,7 @@ + diff --git a/etc/adminhtml/system/rma.xml b/etc/adminhtml/system/rma.xml new file mode 100644 index 0000000..789e40a --- /dev/null +++ b/etc/adminhtml/system/rma.xml @@ -0,0 +1,48 @@ + + + + + This feature exports Adobe Commerce RMA events to the Faslet backend. It remains inactive on Magento Open Source because the native RMA module is not available there. + + + Magento\Config\Model\Config\Source\Yesno + faslet_connect/rma/export_enabled + + + + Internal Faslet ingestion endpoint that receives raw Adobe Commerce RMA events. + faslet_connect/rma/endpoint_url + validate-url + + 1 + + + + + Optional bearer token used for outbound authentication. + faslet_connect/rma/auth_token + Magento\Config\Model\Config\Backend\Encrypted + + 1 + + + + + Timeout in seconds for outbound RMA export calls. + faslet_connect/rma/timeout + validate-digits validate-greater-than-zero + + 1 + + + + + Select which Adobe Commerce RMA lifecycle events should be exported to Faslet. + faslet_connect/rma/lifecycle_events + Faslet\Connect\Model\System\Config\Source\RmaLifecycleEvent + + 1 + + + + \ No newline at end of file diff --git a/etc/config.xml b/etc/config.xml index 9485701..cfa5551 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -6,6 +6,11 @@ v1.1.0 0 + + 0 + 5 + created,return_received,approved,rejected,closed + entity_id sku diff --git a/etc/di.xml b/etc/di.xml index b7c7070..19499a6 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -29,5 +29,5 @@ FasletErrorMonolog - + diff --git a/etc/events.xml b/etc/events.xml new file mode 100644 index 0000000..e97a621 --- /dev/null +++ b/etc/events.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file