Location Availability is changing with Inventory Changes

Hello, I am using Square SDK and posting inventory changes for products by location. I grab the inventory at location A OR Location B, then when I do a transaction on my back office system, I use the tool to reduce inventory at a location, or if I refund something, I will then add inventory at the location. I’ve got six locations.

My problem is I’m running into the situation where after I post an update to an inventory at 1 location, it changes the LOCATION AVAILABILITY OF A PRODUCT. I want to IGNORE the product availability at a location. I do not want it to change. But every time I make an inventory change at a location, the AVILABILITY is also changes. API is working fine where I am posting items, and quantity to go up or down. After a transaction is completed, I’m finding myself with a location availability challenge.

Here is what we are doing. Can you tell me what is missing here?

$client = new SquareClient([
‘accessToken’ => OUR_API_TOKEN
‘environment’ => Environment::PRODUCTION,
]);

		$inventoryApi = $client->getInventoryApi();			

		$body = new BatchChangeInventoryRequest;
		$body->setIdempotencyKey(uniqid('MCCWC-'));
		$body_changes = [];

		$body_changes[0] = new InventoryChange;
		$body_changes[0]->setType(InventoryChangeType::ADJUSTMENT);
		$body_changes[0]->setAdjustment(new InventoryAdjustment);
		$body_changes[0]->getAdjustment()->setCatalogObjectId( $item_variation_id );
		$body_changes[0]->getAdjustment()->setFromState( $from_state );
		$body_changes[0]->getAdjustment()->setToState( $to_state );
		$body_changes[0]->getAdjustment()->setLocationId( $location_id );
		$body_changes[0]->getAdjustment()->setQuantity( $qty );
		$body_changes[0]->getAdjustment()->setOccurredAt(date('c'));

		$body->setChanges($body_changes);

		$body->setIgnoreUnchangedCounts(true);

		$apiResponse = $inventoryApi->batchChangeInventory($body);

		if ($apiResponse->isSuccess()) {
		    $res = $apiResponse->getResult();
		} else {
		    $res = $apiResponse->getErrors();
		}

STATE CHANGE VARIABLES for above code :
To mark qty as sold:

from : InventoryState::IN_STOCK
to: InventoryState::SOLD

To re-stock qty back to the respective location from where we took off (marked as sold) :
from: InventoryState::UNLINKED_RETURN
to: InventoryState::IN_STOCK

:wave: I’m Square Dev AI, here to help answer your question!
Based on the information you’ve provided, the following documentation will help answer your question:

Additional Documentation

Inventory API
Enable Stock Conversion
Locations API

If this documentation helped you find a solution, please check the box indicating that this reply solves the problem.
Otherwise one of our Developer Advocates will provide additional assistance shortly.

The issue you’re experiencing is likely due to the fact that Square’s inventory system is designed to automatically adjust the availability of an item at a location based on the inventory count. When the inventory count for an item at a location reaches zero, the item is marked as sold out at that location, which could be affecting the availability of the item.

However, you can control the visibility of an item at a location using the present_at_location_ids and absent_at_location_ids fields of the CatalogItem object. By setting these fields, you can manually control which locations an item is available at, regardless of the inventory count.

In your case, after making an inventory change, you could make an additional API call to update the CatalogItem object for the item, setting the present_at_location_ids field to ensure the item remains available at the desired locations.

Remember that any changes to the CatalogItem object should be made using the Catalog API’s UpsertCatalogObject endpoint. Also, keep in mind that these changes will affect the visibility of the item at the specified locations across all Square applications, including the Square Dashboard and Point of Sale app.

This answer was reviewed by @Bryan-Square.

But this is not the case that is happening here. As the moment we add location again, we get the inventory count of that location.

For eg: We have a item available at all locations and after some time or few hours we see that the same item is now not available at many locations, where as during this period we made several API requests for inventory change (InventoryState::IN_STOCK - InventoryState::SOLD or InventoryState::UNLINKED_RETURN - InventoryState::IN_STOCK).

Now, when we see this item and it is not available at many locations (randomly) and we again make it availabe at all locations, it also increases stock qty as per locations, where as according to you it should show us ZERO stock fro this particular location as because of that it made it unavailable.

Here, I do understand that an additional API request will force it to be available at all locations, but this is not the solution as doing so will be performance concerning. Like making 2 API request for each order.

Also, the logic is still not clear.

Please help.

Thanks!

Is the item being updated by the seller that’s making the item no longer available for the location. Also you can listen to catalog.updated webhook events to listen for when item have been updated. :slightly_smiling_face:

Actually the reason is not clear like what is making it unavailable at locations and to what location, How and when ?
It is not clear and I am not able to regenerate the issue.
Yes we can try integrating webhooks and do appropriate changes as needed.
I want to regenerate the issue.
May be someone from SQUARE TECH team can look into our account and see logs or something to check what exactly making these changes.

Do you have an item_id that changed and an approximate time frame? We would need to narrow this down to look at the logs. Also there are a lot of places where the availability of an item can be updated. :slightly_smiling_face:

Sure SKU : 736530891930
This is one Item for your reference there are many others as well.
Time frame is not very sure but it was around 21st Nov 2024 may be.
Please check any help would be helpful here :slight_smile:

Hi @smthur ,
Stepping in for Bryan here.

I’d need one more thing: The related merchant Square account’s merchant id or location id.
Thanks for the SKU (I could try to start investigating with this and a location id).

Optionally: If I could get the Catalog id instead of the SKU. What is the API catalog id of one of these items where the qty randomly reduced hours later? If you’re in the Seller Dashboard, if you click into the item, if you could give me the URL will have the ID I’m looking for.

I want to update only present_at_all_locations of item. But in reference it is doing a lot
Update Catalog Objects.
Kindly suggest.

Also, could there be problem with below code as here I set locations to get Inventory counts ?

		$body = new BatchRetrieveInventoryCountsRequest();
		$body->setCatalogObjectIds( $ITEM_VARIATION_IDs );
		$body->setLocationIds( SELF::$WAREHOUSE_LOCATION );

		$inventoryApi = $client->getInventoryApi();
		$apiResponse = $inventoryApi->batchRetrieveInventoryCounts($body);

Sure, I will share details you want. But is it not too much of information here, which is public ?

Also, I am trying to use API to update Item for Location only but it seems it also reqyires me to update price as well ?

Below is the code for your reference :

          $price_money = new \Square\Models\Money();
          $price_money->setAmount(76);
          $price_money->setCurrency('USD');
          
          $item_variation_data = new \Square\Models\CatalogItemVariation();
          $item_variation_data->setItemId('#ID_STRING');
          $item_variation_data->setPricingType('FIXED_PRICING');
          $item_variation_data->setPriceMoney($price_money);
          
          $object = new \Square\Models\CatalogObject('ID_STRING');
          $object->setType('ITEM_VARIATION');
          $object->setVersion(VERSION_STRING);
          $object->setPresentAtAllLocations(true);
          $object->setItemVariationData($item_variation_data);
          
          $body = new \Square\Models\UpsertCatalogObjectRequest('RANDOM UNIQUE STRING', $object);
          
          $api_response = $client->getCatalogApi()->upsertCatalogObject($body);
          
          if ($api_response->isSuccess()) {
              $result = $api_response->getResult();
          } else {
              $errors = $api_response->getErrors();
          }

Please suggest!

Sure, I will share details you want. But is it not too much of information here, which is public ?

Application id, location ids, and merchant ids are safe to share publicly. What should never be shared is the access token. But if you’d like, we can continue the conversation over email. You can reach the Developer Success Engineers through the Support dropdown options in the Square Documentation. To start an email, you can choose “Contact Support”. Ask for Lance and link this forum discussion.

I want to update only present_at_all_locations of item.
…Also, I am trying to use API to update Item for Location only but it seems it also requires me to update price as well ?

The Catalog API’s Upsert is a bit difficult to work with. I know you’ve referred to this throughout this discussion, so sorry if I’m missing your point. If I misunderstanding, looking at the history of an example catalog id will help me later.

The general trick for catalog upsert is to just RetrieveCatalogObject right before the upsert. Just remove any read only fields, like timestamps, and just change what you’re looking to change.

Can you clarify what exactly you’re trying to do?

  • What is present_at_all_locations currently set to.
  • What would you like it to be? Where is the item present or absent from?

It is a bit complicated, since if this field changes, another field like present_at_location_ids might need to be adjusted to then clarify where the item is present.

You shouldn’t need to update price if you do not want to. You can adjust an example reference code to suit your needs. You should be able to remove the two pricing code lines from that reference.

Also, could there be problem with below code as here I set locations to get Inventory counts ?

Nothing odd sticks out to me at a glance.