Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ runs:
- name: Install dependencies
shell: bash
run: |
composer install --no-dev --optimize-autoloader
composer install --optimize-autoloader

- name: Run unit tests
shell: bash
run: |
vendor/bin/phpunit Test/Unit/ --testdox

- name: Run integration tests
shell: bash
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ jobs:

- name: Install dependencies
run: |
composer install --no-dev --optimize-autoloader
composer install --optimize-autoloader

- name: Run Unit Tests
run: |
vendor/bin/phpunit Test/Unit/ --testdox

- name: Run Integration Tests
run: |
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
.PHONY: test test-local test-compatibility lint lint-fix help
.PHONY: test test-local test-unit test-compatibility lint lint-fix help

# Default target
help:
@echo "Available commands:"
@echo " make test - Run all tests using Docker"
@echo " make test-local - Run all tests locally (requires PHP)"
@echo " make test-unit - Run unit tests only"
@echo " make test-compatibility - Run Adobe Commerce 2.4.8-p1 compatibility tests"
@echo " make lint - Run PHP CodeSniffer linting"
@echo " make lint-fix - Auto-fix linting issues where possible"
Expand All @@ -18,11 +19,17 @@ test:
# Run tests locally (requires PHP)
test-local:
@echo "Running all tests locally..."
@vendor/bin/phpunit Test/Unit/ --testdox
@for test_file in Test/Integration/*.php; do \
echo "Running $$test_file..."; \
php "$$test_file"; \
done

# Run unit tests only
test-unit:
@echo "Running unit tests..."
@vendor/bin/phpunit Test/Unit/ --testdox

# Run Adobe Commerce 2.4.8-p1 compatibility tests
test-compatibility:
@echo "Running Adobe Commerce 2.4.8-p1 compatibility tests..."
Expand Down
53 changes: 24 additions & 29 deletions Model/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ class Api
*/
private $cartItemResponseHandler;

/**
* Product TIC Service
*
* @var \Taxcloud\Magento2\Model\ProductTicService
*/
private $productTicService;

/**
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\App\CacheInterface $cacheType
Expand All @@ -125,6 +132,9 @@ class Api
* @param \Magento\Catalog\Model\ProductFactory $productFactory
* @param \Magento\Directory\Model\RegionFactory $regionFactory
* @param \Taxcloud\Magento2\Logger\Logger $tclogger
* @param SerializerInterface $serializer
* @param \Taxcloud\Magento2\Model\CartItemResponseHandler $cartItemResponseHandler
* @param \Taxcloud\Magento2\Model\ProductTicService $productTicService
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
Expand All @@ -137,7 +147,8 @@ public function __construct(
\Magento\Directory\Model\RegionFactory $regionFactory,
\Taxcloud\Magento2\Logger\Logger $tclogger,
SerializerInterface $serializer,
\Taxcloud\Magento2\Model\CartItemResponseHandler $cartItemResponseHandler
\Taxcloud\Magento2\Model\CartItemResponseHandler $cartItemResponseHandler,
\Taxcloud\Magento2\Model\ProductTicService $productTicService
) {
$this->_scopeConfig = $scopeConfig;
$this->_cacheType = $cacheType;
Expand All @@ -148,6 +159,7 @@ public function __construct(
$this->_regionFactory = $regionFactory;
$this->serializer = $serializer;
$this->cartItemResponseHandler = $cartItemResponseHandler;
$this->productTicService = $productTicService;
if ($scopeConfig->getValue('tax/taxcloud_settings/logging', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)) {
$this->_tclogger = $tclogger;
} else {
Expand Down Expand Up @@ -186,23 +198,6 @@ protected function _getGuestCustomerId()
return $this->_scopeConfig->getValue('tax/taxcloud_settings/guest_customer_id', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ?? '-1';
}

/**
* Get TaxCloud Default Product TIC
* @return string
*/
protected function _getDefaultTic()
{
return $this->_scopeConfig->getValue('tax/taxcloud_settings/default_tic', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ?? '00000';
}

/**
* Get TaxCloud Default Shipping TIC
* @return string
*/
protected function _getShippingTic()
{
return $this->_scopeConfig->getValue('tax/taxcloud_settings/shipping_tic', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ?? '11010';
}

/**
* Get TaxCloud Cache Lifetime
Expand Down Expand Up @@ -331,16 +326,14 @@ public function lookupTaxes($itemsByType, $shippingAssignment, $quote)
if (isset($itemsByType[self::ITEM_TYPE_PRODUCT])) {
foreach ($itemsByType[self::ITEM_TYPE_PRODUCT] as $code => $itemTaxDetail) {
$item = $keyedAddressItems[$code];
if ($item->getProduct()->getTaxClassId() === '0') {
if ($item->getProduct() && $item->getProduct()->getTaxClassId() === '0') {
// Skip products with tax_class_id of None, store owners should avoid doing this
continue;
}
$productModel = $this->_productFactory->create()->load($item->getProduct()->getId());
$tic = $productModel->getCustomAttribute('taxcloud_tic');
$cartItems[] = array(
'ItemID' => $item->getSku(),
'Index' => $index,
'TIC' => $tic ? $tic->getValue() : $this->_getDefaultTic(),
'TIC' => $this->productTicService->getProductTic($item, 'lookupTaxes'),
'Price' => $item->getPrice() - $item->getDiscountAmount() / $item->getQty(),
'Qty' => $item->getQty(),
);
Expand All @@ -354,7 +347,7 @@ public function lookupTaxes($itemsByType, $shippingAssignment, $quote)
$cartItems[] = array(
'ItemID' => 'shipping',
'Index' => $index++,
'TIC' => $this->_getShippingTic(),
'TIC' => $this->productTicService->getShippingTic(),
'Price' => $itemTaxDetail[self::KEY_ITEM]->getRowTotal(),
'Qty' => 1,
);
Expand Down Expand Up @@ -604,12 +597,10 @@ public function returnOrder($creditmemo)
if ($items) {
foreach ($items as $creditItem) {
$item = $creditItem->getOrderItem();
$productModel = $this->_productFactory->create()->load($item->getProduct()->getId());
$tic = $productModel->getCustomAttribute('taxcloud_tic');
$cartItems[] = array(
'ItemID' => $item->getSku(),
'Index' => $index,
'TIC' => $tic ? $tic->getValue() : $this->_getDefaultTic(),
'TIC' => $this->productTicService->getProductTic($item, 'returnOrder'),
'Price' => $creditItem->getPrice() - $creditItem->getDiscountAmount() / $creditItem->getQty(),
'Qty' => $creditItem->getQty(),
);
Expand All @@ -623,7 +614,7 @@ public function returnOrder($creditmemo)
$cartItems[] = array(
'ItemID' => 'shipping',
'Index' => $index,
'TIC' => $this->_getShippingTic(),
'TIC' => $this->productTicService->getShippingTic(),
'Price' => $shippingAmount,
'Qty' => 1,
);
Expand Down Expand Up @@ -707,8 +698,12 @@ public function returnOrder($creditmemo)

$returnResult = $obj->getResult();

if ($returnResult['ResponseType'] != 'OK') {
$this->_tclogger->info('Error encountered during returnOrder: ' . $returnResult['Messages']['ResponseMessage']['Message']);
if (!$returnResult || $returnResult['ResponseType'] != 'OK') {
$errorMessage = 'Unknown error';
if ($returnResult && isset($returnResult['Messages']['ResponseMessage']['Message'])) {
$errorMessage = $returnResult['Messages']['ResponseMessage']['Message'];
}
$this->_tclogger->info('Error encountered during returnOrder: ' . $errorMessage);
return false;
}

Expand Down
138 changes: 138 additions & 0 deletions Model/ProductTicService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php
/**
* Taxcloud_Magento2
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
*
* @package Taxcloud_Magento2
* @author TaxCloud <service@taxcloud.net>
* @copyright 2021 The Federal Tax Authority, LLC d/b/a TaxCloud
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/

namespace Taxcloud\Magento2\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Catalog\Model\ProductFactory;
use Taxcloud\Magento2\Logger\Logger;

/**
* Service for handling Product TIC (Taxability Information Code) logic
* Handles cases where products have been deleted or don't have custom TIC attributes
*/
class ProductTicService
{
/**
* Default TIC fallback value when configuration is empty or null
*/
const DEFAULT_TIC = '00000';

/**
* Default shipping TIC fallback value when configuration is empty or null
*/
const DEFAULT_SHIPPING_TIC = '11010';
/**
* @var ScopeConfigInterface
*/
private $scopeConfig;

/**
* @var ProductFactory
*/
private $productFactory;

/**
* @var Logger
*/
private $logger;

/**
* @param ScopeConfigInterface $scopeConfig
* @param ProductFactory $productFactory
* @param Logger $logger
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
ProductFactory $productFactory,
Logger $logger
) {
$this->scopeConfig = $scopeConfig;
$this->productFactory = $productFactory;
$this->logger = $logger;
}

/**
* Get product TIC (Taxability Information Code) with null safety
* Handles cases where product has been deleted or doesn't have custom TIC
*
* @param \Magento\Sales\Model\Order\Item $item
* @param string $context Context for logging (e.g., 'lookupTaxes', 'returnOrder')
* @return string The TIC value
*/
public function getProductTic($item, $context = '')
{
$product = $item->getProduct();

// Handle case where product has been deleted
if (!$product || !$product->getId()) {
$this->logger->info(
'Product not found for item ' . $item->getSku() . ' in ' . $context . ', using default TIC'
);
return $this->getDefaultTic();
}

$productModel = $this->productFactory->create()->load($product->getId());
$tic = $productModel->getCustomAttribute('taxcloud_tic');

return $tic ? $tic->getValue() : $this->getDefaultTic();
}

/**
* Get the default TIC value from configuration
* Falls back to DEFAULT_TIC if configuration is empty or null
*
* @return string
*/
public function getDefaultTic()
{
$value = $this->scopeConfig->getValue(
'tax/taxcloud_settings/default_tic',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);

return ($value !== null && $value !== '') ? $value : self::DEFAULT_TIC;
}

/**
* Check if a product exists and is valid
*
* @param \Magento\Sales\Model\Order\Item $item
* @return bool
*/
public function isProductValid($item)
{
$product = $item->getProduct();
return $product && $product->getId();
}

/**
* Get the shipping TIC value from configuration
* Falls back to DEFAULT_SHIPPING_TIC if configuration is empty or null
*
* @return string
*/
public function getShippingTic()
{
$value = $this->scopeConfig->getValue(
'tax/taxcloud_settings/shipping_tic',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);

return ($value !== null && $value !== '') ? $value : self::DEFAULT_SHIPPING_TIC;
}
}
Loading
Loading