surprisehighway / craft-avatax Goto Github PK
View Code? Open in Web Editor NEWCalculate and add sales tax to an order's base tax using Avalara's AvaTax service with Craft Commerce 5.
License: MIT License
Calculate and add sales tax to an order's base tax using Avalara's AvaTax service with Craft Commerce 5.
License: MIT License
Hi there,
Recently found a bug in our project I'm hoping you can help me figure out. Launched a site a few months ago using Craft-Avatax with Craft 3.3 and Commerce 2.2.15 - things work great!
I've upgraded our dev and staging environments to Craft 3.4.21 and Commerce to 3.1.5. Existing test accounts in our system can place orders (processed by the Stripe sandbox) perfectly fine. New user accounts however are hitting a TypeError on checkout:
Argument 1 passed to surprisehighway\avatax\services\SalesTaxService::validateAddress() must be an instance of craft\commerce\models\Address, null given, called in /craft/vendor/surprisehighway/craft-avatax/src/services/SalesTaxService.php on line 553
I had to refactor some of my address input/edit form due to some changes in Commerce , but I haven't touched anything else.
Any thoughts or ideas would be greatly appreciated. Thank you!
Hey Guys,
Great plugin!
I happened to notice that the sandboxLicenseKey
and licenseKey
value is getting stored into the project configuration file (/config/project/project.yaml
). A solution to avoid committing sensitive values into version control is to use an environmental configuration.
Plugin settings that may need to change per-environment, or contain sensitive information, should be implemented as environmental settings.
For example, the current Avatax definition is:
avatax:
edition: standard
enabled: true
schemaVersion: 2.0.0
settings:
accountId: ''
certCaptureClientId: null
certCapturePassword: null
certCaptureUsername: null
companyCode: ''
debug: ''
defaultDiscountCode: OD010000
defaultShippingCode: FR
defaultTaxCode: P0000000
enableAddressValidation: ''
enableCommitting: ''
enablePartialRefunds: ''
enableTaxCalculation: ''
environment: sandbox
licenseKey: ''
sandboxAccountId: ''
sandboxCompanyCode: ''
sandboxLicenseKey: ''
shipFromCity: ''
shipFromCountry: ''
shipFromName: ''
shipFromState: ''
shipFromStreet1: ''
shipFromStreet2: ''
shipFromStreet3: ''
shipFromZipCode: ''
With the environment settings specified above, that could turn into:
avatax:
edition: standard
enabled: true
schemaVersion: 2.0.0
settings:
...
licenseKey: '$AVALARA_LICENSE_KEY'
sandboxLicenseKey: '$AVALARA_SANDBOX_LICENSE_KEY'
...
Assuming you were to set the values
AVALARA_LICENSE_KEY
AVALARA_SANDBOX_LICENSE_KEY
in your/.env
file.
Throwing an exception when attempting to save user preferences in the Control Panel. Not saving any address info but it appears to be triggering the onBeforeSaveAddress event. Stack trace:
yii\base\Exception: Invalid address. in /Users/joel/Sites/craft3/vendor/surprisehighway/craft-avatax/src/services/SalesTaxService.php:380
Stack trace:
#0 /Users/joel/Sites/craft3/vendor/surprisehighway/craft-avatax/src/Avatax.php(176): surprisehighway\avatax\services\SalesTaxService->validateAddress(Object(craft\commerce\models\Address))
#1 /Users/joel/Sites/craft3/vendor/surprisehighway/craft-avatax/src/Avatax.php(131): surprisehighway\avatax\Avatax->onBeforeSaveAddress(Object(craft\commerce\events\AddressEvent))
#2 [internal function]: surprisehighway\avatax\Avatax->surprisehighway\avatax\{closure}(Object(craft\commerce\events\AddressEvent))
#3 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Event.php(312): call_user_func(Object(Closure), Object(craft\commerce\events\AddressEvent))
#4 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Component.php(636): yii\base\Event::trigger('craft\\commerce\\...', 'beforeSaveAddre...', Object(craft\commerce\events\AddressEvent))
#5 /Users/joel/Sites/craft3/vendor/craftcms/commerce/src/services/Addresses.php(200): yii\base\Component->trigger('beforeSaveAddre...', Object(craft\commerce\events\AddressEvent))
#6 /Users/joel/Sites/craft3/vendor/craftcms/commerce/src/elements/Order.php(927): craft\commerce\services\Addresses->saveAddress(Object(craft\commerce\models\Address), false)
#7 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/services/Elements.php(515): craft\commerce\elements\Order->afterSave(false)
#8 /Users/joel/Sites/craft3/vendor/craftcms/commerce/src/services/Customers.php(426): craft\services\Elements->saveElement(Object(craft\commerce\elements\Order))
#9 [internal function]: craft\commerce\services\Customers->saveUserHandler(Object(craft\events\ModelEvent))
#10 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Event.php(312): call_user_func(Array, Object(craft\events\ModelEvent))
#11 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Component.php(636): yii\base\Event::trigger('craft\\elements\\...', 'afterSave', Object(craft\events\ModelEvent))
#12 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/base/Element.php(1889): yii\base\Component->trigger('afterSave', Object(craft\events\ModelEvent))
#13 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/elements/User.php(1322): craft\base\Element->afterSave(false)
#14 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/services/Elements.php(515): craft\elements\User->afterSave(false)
#15 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/controllers/UsersController.php(1088): craft\services\Elements->saveElement(Object(craft\elements\User), false)
#16 [internal function]: craft\controllers\UsersController->actionSaveUser()
#17 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#18 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#19 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/web/Controller.php(109): yii\base\Controller->runAction('save-user', Array)
#20 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Module.php(528): craft\web\Controller->runAction('save-user', Array)
#21 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/web/Application.php(297): yii\base\Module->runAction('users/save-user', Array)
#22 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/web/Application.php(561): craft\web\Application->runAction('users/save-user', Array)
#23 /Users/joel/Sites/craft3/vendor/craftcms/cms/src/web/Application.php(281): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#24 /Users/joel/Sites/craft3/vendor/yiisoft/yii2/base/Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#25 /Users/joel/Sites/craft3/web/index.php(21): yii\base\Application->run()
#26 {main}
Hey Guys,
There's a bug with the address validation controller endpoint: actions/avatax/json/validate-address
Before making your plugin compatible with Commerce v4, you had the following method inside of SalesTaxService.php
:
craft-avatax/src/services/SalesTaxService.php
Line 723 in 9c0a6c7
Since Craft Commerce no longer controls Addresses (they are a part of Craft CMS now), the model for Address changed. In doing so, the
stateId
/stateText
field is no longer used.
You can read more on countries and states here.
It's my understanding that administrativeArea
is now the appropriate field on the Address model for what most people consider a state / province. From the Craft CMS link above:
While Commerce has removed support for managing custom countries and states, the commerce/upgrade command prompts you to map the custom countries to real country codes, and copies the state abbreviation (if a custom state was created) or the stateName entered by the customer to the administrativeArea field on relevant addresses and zones.
So it looks like all we need to do is update $state = $this->getState($address);
to be $state = $address->administrativeArea
:
craft-avatax/src/services/SalesTaxService.php
Line 759 in c497b63
This came up during certification. Full refunds are fine because they are tied to the original order as a Return Invoice, but partial refunds tied to an order need to have updated line items or be issued as a percentage, so we are issuing a Return Invoice for the arbitrary amount to the customer instead. Store owners should be made aware of the difference and be allowed to choose if they want to send partial refunds to Avalara.
I am getting the following error when trying to validate an address with the AJAX sample code provided in the documentation: Failed to load resource: the server responded with a status of 400 (Bad Request). /actions/avatax/json/validate-address
I believe the issue is the "url" setting in the ajax code; I do not know where to point this or if it needs to remain the same.
Here is the js code being used (provided from the documentation):
$('#address-form').on('submit.addressValidation', function(e) {
e.preventDefault();
var $form = $(this);
var data = {
address1 : $('[name="shippingAddress[address1]"]').val(),
address2 : $('[name="shippingAddress[address2]"]').val(),
city : $('[name="shippingAddress[city]"]').val(),
zipCode : $('[name="shippingAddress[zipCode]"]').val(),
stateValue : $('[name="shippingAddress[stateValue]"]').val(),
countryId : $('[name="shippingAddress[countryId]"]').val()
};
var csrfTokenName = "{{ craft.app.config.general.csrfTokenName }}";
var csrfTokenValue = "{{ craft.app.request.csrfToken }}";
data[csrfTokenName] = csrfTokenValue;
$.ajax({
type: 'post',
url: '/actions/avatax/json/validate-address',
data: data,
dataType: 'json'
}).done(function(data){
console.log(data);
if(data.success) {
// valid address
$form.off('submit.addressValidation').submit();
} else {
// handle error here...
return false;
}
});
});
I am trying to install an update to Enupal Translated and getting this error on composer update:
Problem 1
- Root composer.json requires enupal/translate 2.3.0 -> satisfiable by enupal/translate[2.3.0].
- Conclusion: don't install guzzlehttp/guzzle 7.4.0 (conflict analysis result)
- Conclusion: don't install guzzlehttp/guzzle 7.4.2 (conflict analysis result)
- enupal/translate 2.3.0 requires viniciusgava/google-translate-api 3.1.0 -> satisfiable by viniciusgava/google-translate-api[3.1.0].
- Root composer.json requires surprisehighway/craft-avatax 2.1.8 -> satisfiable by surprisehighway/craft-avatax[2.1.8].
- guzzlehttp/guzzle 7.4.x-dev is an alias of guzzlehttp/guzzle dev-master and thus requires it to be installed too.
- viniciusgava/google-translate-api 3.1.0 requires guzzlehttp/guzzle ^7.0.1 -> satisfiable by guzzlehttp/guzzle[7.0.1, ..., 7.4.x-dev (alias of dev-master)].
- You can only install one version of a package, so only one of these can be installed: guzzlehttp/guzzle[dev-master, v1.0.3, v1.0.4, v2.0.0, ..., v2.8.8, v3.0.0, ..., v3.8.1, 4.0.0-rc.1, ..., 4.x-dev, 5.0.0, ..., 5.3.x-dev, 6.0.0, ..., 6.5.x-dev, 7.0.0-beta.1, ..., 7.4.x-dev].
- Conclusion: install guzzlehttp/guzzle 6.5.4 (conflict analysis result)
Which relates to this recent update: enupal/translate#43
Can you update your Guzzle requirement so I can update Enupal Translate?
Craft Version: 3.6.14
Craft Commerce Version: 3.2.14.1
Craft Avatax Version: 2.1.3
PHP Version: 8.0.2
When running craft-avatax
in a production context ('devMode' => false
), we get an internal server error because the variable $model
is undefined. Specifically speaking, the errors happens here when $model
is used within the logging statement.
The source of the problem is that $model
is only defined when devMode is enabled. See here.
This issue is related to #34
We got a notice from Avatax that SSL cert updates are required for custom integration.
Is this plugin considered a custom integration? Do we need to do anything on our site?
Thanks!
Just hoping to get a pulse on if it's on the roadmap!
All input fields in the settings are readonly and don't allow me to add text
I'm connecting properly to the plugin, but I'm returning $0 in shipping. When I look at the response generated in the logs, the problem seems to be that isItemTaxable
is set to false on all line items, which causes the cart amount to come through as exempt.
I'm not doing anything to declare items non-taxable, so I'm not sure why they are.
I do have a custom adjuster in place to handle quantity based discounts, but it wasn't interfering with Craft Commerce's default tax calculations.
We have automated testing setup on our Craft + Commerce install. Once we added this plugin, the test suite no longer runs completely (it doesn't install other plugins we have installed). I commented this out just to test:
Lines 98 to 106 in 67c93a8
And the suite runs correctly then. I'm not sure the best solution to fix this. Should this be moved up to the top of the method?
Lines 307 to 309 in 67c93a8
Or maybe check to see if the env is test
?
Craft Pro 3.6.11.1
Avatax 2.1.5
Craft Version: 3.6.14
Craft Commerce Version: 3.2.14.1
Craft Avatax Version: 2.1.3
PHP Version: 8.0.2
commerce/customer-addresses/save
, I get the following error:Exception – yii\base\Exception
Request could not be completed in .../vendor/surprisehighway/craft-avatax/src/services/SalesTaxService.php at line 723
The issue is that my tax code value of PA3000100
is somehow being converted to {"label":"PA3000100","value":"PA3000100","selected":true,"valid":true}
from $t->withLine().
I realize $t
is actually an instance of vendor/avalara/avataxclient/src/TransactionBuilder.php
.
I was able to verify this issue by using the built in logger for the plugin. These were my modifications to SalesTaxService.php:614 and TransactionBuilder.php:446
# SalesTaxService.php#L614
Avatax::info(__FUNCTION__.'(): $taxCode is '.$taxCode);
// amount, $quantity, $itemCode, $taxCode)
$t = $t->withLine(
$lineItem->subtotal, // Total amount for the line item
$lineItem->qty, // Quantity
$itemCode, // Item Code
$taxCode // Tax Code - Default or Custom Tax Code.
);
Avatax::info(__FUNCTION__.'(): $taxCode is '.$taxCode);
# I added the getModel method for logging purposes
Avatax::info(json_encode($t->getModel()));
# TransactionBuilder.php:446
Avatax::info(__FUNCTION__.'(): Inspect tax code value before creating array: '.$taxCode);
$l = [
'number' => $lineNumber,
'quantity' => $quantity,
'amount' => $amount,
'taxCode' => $taxCode,
'itemCode' => $itemCode
];
Avatax::info(__FUNCTION__.'(): Inspect tax code value after creating array: '.$taxCode);
Avatax::info(__FUNCTION__.'(): Value of $l that was just created '.json_encode($l));
Here is the corresponding log entries:
[Info] getTotalTax(): $taxCode is PA3000100
[Info] withLine(): Value of $l that was just created {"number":1,"quantity":6,"amount":3090,"taxCode":{"label":"PA3000100","value":"PA3000100","selected":true,"valid":true},"itemCode":"NBL-244-UED90-45L"}
[Info] withLine(): Inspect tax code value after creating array: PA3000100
[Info] withLine(): Inspect tax code value before creating array: PA3000100
[Info] getTotalTax(): $taxCode is PA3000100
Note the logs are in reverse order since they were coming from Craft's Control Panel
The problem is the value of $taxCode
somehow turns into {"label":"PA3000100","value":"PA3000100","selected":true,"valid":true}
.
I figured out that if you wrap the $taxCode
parameter with strval()
, then the issue doesn't happen and the request succeeds for TransactionBuilder.php:446
$l = [
'number' => $lineNumber,
'quantity' => $quantity,
'amount' => $amount,
'taxCode' => strval($taxCode),
'itemCode' => $itemCode
];
Here is the full stack trace:
Hi There! I was curious if this plugin supports tax & fee calculations for international purchases.
It didn't seem like it from a quick browse of the source code, but I thought I'd check.
Currently the Avatax plugin commits an order when the order is marked complete. However, we're finding there are situations where this causes issues:
These are just a few examples. I think it would be beneficial if there were an option in the Avatax plugin settings to select which order statuses trigger an order being committed. This would allow users to ensure that orders are only committed when they have been shipped/approved, and weed out scenarios above such as cancellations and edits to orders.
This a request for an enhancement.
When Tax Calculation is enabled, any cart update causes a tax calculation to run. We have come across a variety of use cases where having to wait for the Tax Adjusters to run because of the communication with the AvaTax API will produce a poor user experience because of the difference between the time a request is made to add something to a cart, and when the request completes.
Instead, we would like to have some programmatic way to manually tell the Tax Calculation to run the Adjuster for the cart. This would be ideal because only the last few pageviews of our sales funnel rely on needing accurate Tax information. We could circumvent the many round trips by only running the Tax Adjuster on the cart when needed.
Is there currently a way to do this? My initial thoughts were to turn Tax Calculation off in the settings and then call some method. However, I didn't see anything in the readme related to manual actions needing to be taken.
As an aside, my suspicion is that Tax Calculation from CP and non-CP requests should likely be treated differently when a use case like above is used. Users on a site would only need the Tax Adjuster to run towards the end of the checkout process. However, operations personnel likely need to have Tax Adjusters run when any updates to a cart trigger a recalculation from within the Control Panel (CP).
Is there a timeline for Craft cms 4 support?
blog article announcing new version : https://craftcms.com/blog/craft-4
Hi,
We updated the plugin to 2.1.7 version and when order is saved through a console command the Avatax plugin causes this error Exception 'yii\base\UnknownMethodException' with message 'Calling unknown method: craft\console\Request::getParam()' in /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Component.php:301
Stack trace:
Exception 'yii\base\UnknownMethodException' with message 'Calling unknown method: craft\console\Request::getParam()'
in /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Component.php:301
Stack trace:
#0 /home/forge/solspace.magna-power.com/vendor/surprisehighway/craft-avatax/src/services/SalesTaxService.php(787): yii\base\Component->__call()
#1 /home/forge/solspace.magna-power.com/vendor/surprisehighway/craft-avatax/src/services/SalesTaxService.php(106): surprisehighway\avatax\services\SalesTaxService->parseOverrideParam()
#2 /home/forge/solspace.magna-power.com/vendor/surprisehighway/craft-avatax/src/adjusters/AvataxTaxAdjuster.php(24): surprisehighway\avatax\services\SalesTaxService->createSalesOrder()
#3 /home/forge/solspace.magna-power.com/vendor/craftcms/commerce/src/elements/Order.php(1828): surprisehighway\avatax\adjusters\AvataxTaxAdjuster->adjust()
#4 /home/forge/solspace.magna-power.com/plugins/magnapowerbusinessfunctions/src/MagnaPowerBusinessFunctions.php(210): craft\commerce\elements\Order->recalculate()
#5 [internal function]: solspace\magnapowerbusinessfunctions\MagnaPowerBusinessFunctions->solspace\magnapowerbusinessfunctions{closure}()
#6 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Event.php(312): call_user_func()
#7 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Component.php(637): yii\base\Event::trigger()
#8 /home/forge/solspace.magna-power.com/vendor/craftcms/cms/src/base/Element.php(4102): yii\base\Component->trigger()
#9 /home/forge/solspace.magna-power.com/vendor/craftcms/commerce/src/elements/Order.php(2071): craft\base\Element->afterSave()
#10 /home/forge/solspace.magna-power.com/vendor/craftcms/cms/src/services/Elements.php(2687): craft\commerce\elements\Order->afterSave()
#11 /home/forge/solspace.magna-power.com/vendor/craftcms/cms/src/services/Elements.php(783): craft\services\Elements->_saveElementInternal()
#12 /home/forge/solspace.magna-power.com/plugins/magnapowerbusinessfunctions/src/services/Data.php(589): craft\services\Elements->saveElement()
#13 /home/forge/solspace.magna-power.com/plugins/magnapowerbusinessfunctions/src/services/Data.php(478): solspace\magnapowerbusinessfunctions\services\Data->processOrderFile()
#14 /home/forge/solspace.magna-power.com/plugins/magnapowerbusinessfunctions/src/console/controllers/DataController.php(56): solspace\magnapowerbusinessfunctions\services\Data->processInbox()
#15 [internal function]: solspace\magnapowerbusinessfunctions\console\controllers\DataController->actionProcessInbox()
#16 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#17 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Controller.php(181): yii\base\InlineAction->runWithParams()
#18 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/console/Controller.php(184): yii\base\Controller->runAction()
#19 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Module.php(534): yii\console\Controller->runAction()
#20 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/console/Application.php(181): yii\base\Module->runAction()
#21 /home/forge/solspace.magna-power.com/vendor/craftcms/cms/src/console/Application.php(89): yii\console\Application->runAction()
#22 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/console/Application.php(148): craft\console\Application->runAction()
#23 /home/forge/solspace.magna-power.com/vendor/yiisoft/yii2/base/Application.php(392): yii\console\Application->handleRequest()
#24 /home/forge/solspace.magna-power.com/craft(22): yii\base\Application->run()
#25 {main}
It seems like that with 2.1.7 a bug was introduced for a console environment where the Avatax plugin tries to call a getParam() from a request which isn't possible for the console environment.
When updating an order/cart with a bad address, an uncaught exception is thrown. This results in an error causing Craft to render it's environment specific error page. I noticed this after having a local test user with a crappy address, then installing/configuring the plugin and logging in with that user. It also happens when setting a bad address normally during checkout (tested with default commerce templates).
This seems like a mature plugin and I find it hard to believe that a bad address could have this effect. I'm hoping there is a documentation I missed or I'm doing something dumb to cause this.
Details
Bad cart addresses (with wrong U.S. zip/state) results in this api response(taken from Avatax log):
"Client error: `POST https:\/\/sandbox-rest.avatax.com\/api\/v2\/transactions\/create` resulted in a `400 Bad Request`
response:\n{\"error\":{\"code\":\"GetTaxError\",\"message\":\"Tax calculation cannot be determined. Zip is not valid for the state.\",\"target (truncated...)\n"
Causes uncaught exception here /src/services/SalesTaxService.php at line 714
effectively killing the page.
System report:
PHP version 7.4.15
Craft Pro 3.6.4.1
Craft Commerce 3.2.14.1
Avatax | 2.1.3
We are running into this error. Any idea where this error is coming from or how to fix it?
[Error] Request to avatax.com failed
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.