diff --git a/.github/workflows/action.yaml b/.github/workflows/action.yaml index ca426e5..b85e4a9 100644 --- a/.github/workflows/action.yaml +++ b/.github/workflows/action.yaml @@ -9,5 +9,7 @@ on: jobs: plugin-action: - uses: matmair/inventree-meta-plugin/.github/workflows/plugin_action.yaml@main - secrets: inherit + name: CI + uses: matmair/inventree-meta-plugin/.github/workflows/plugin_action.yaml@next + secrets: + PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 439a8c8..182590c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,20 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks +exclude: .*/(static|migrations|locale)/.* repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - - id: mixed-line-ending -- repo: https://github.com/pycqa/flake8 - rev: '6.0.0' + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - id: mixed-line-ending + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.11 hooks: - - id: flake8 - additional_dependencies: [ - 'flake8-bugbear', - 'flake8-docstrings', - 'flake8-string-format', - 'pep8-naming ', - ] -- repo: https://github.com/pycqa/isort - rev: '5.12.0' + - id: ruff + args: [--fix] + - id: ruff-format + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 hooks: - - id: isort + - id: codespell diff --git a/pyproject.toml b/pyproject.toml index 26c14d4..543160b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" license = {text = "MIT"} keywords = ["inventree", "inventree-plugin", "shopify"] authors = [ - {name = "Matthias J Mair", email = "info@mjmair.com"} + {name = "Matthias Mair", email = "code@mjmair.com"} ] classifiers=[ "Programming Language :: Python :: 3", @@ -30,11 +30,29 @@ dependencies = ['requests', 'django'] dev = ['twine', 'setuptools'] [project.urls] -repository = "https://github.com/matmair/inventree-shopify" -"Bug Tracker" = "https://github.com/matmair/inventree-shopify/issues" +"Repository" = "https://github.com/invenhost/inventree-shopify" +"Bug Tracker" = "https://github.com/invenhost/inventree-shopify/issues" [project.entry-points."inventree_plugins"] inventree-shopify = "inventree_shopify:ShopifyPlugin" [tool.setuptools.packages.find] where = ["src"] + +[tool.ruff] +exclude=[".git","__pycache__","dist","build","test.py","tests", "venv","env",".venv",".env"] + +[tool.ruff.lint] +select = ["A", "B", "C4", "D", "DJ", "N", "I","S"] +ignore = ["N999", ] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.isort] +combine-as-imports = true +section-order = ["future", "standard-library", "django", "third-party", "first-party", "local-folder" ] +known-first-party = ["src", "plugin", "InvenTree", "common"] + +[tool.ruff.lint.isort.sections] +"django" = ["django"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index dd5d75a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[flake8] -ignore = - E501, - # E501: Line too long - D401, - # D401: First line should be in imperative mood -per-file-ignores = - __init__.py:F401 -exclude = */migrations/* diff --git a/src/inventree_shopify/ShopifyPlugin.py b/src/inventree_shopify/ShopifyPlugin.py index 192a8ea..35f2288 100644 --- a/src/inventree_shopify/ShopifyPlugin.py +++ b/src/inventree_shopify/ShopifyPlugin.py @@ -7,26 +7,42 @@ from django.http.response import Http404 from django.shortcuts import redirect, render from django.utils.translation import ugettext_lazy as _ -from plugin import InvenTreePlugin -from plugin.mixins import (APICallMixin, AppMixin, EventMixin, NavigationMixin, - SettingsMixin, UrlsMixin) -from stock.models import StockItem +from stock.models import StockItem -class ShopifyPlugin(EventMixin, APICallMixin, AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, InvenTreePlugin): +from plugin import InvenTreePlugin +from plugin.mixins import ( + APICallMixin, + AppMixin, + EventMixin, + NavigationMixin, + SettingsMixin, + UrlsMixin, +) + + +class ShopifyPlugin( + EventMixin, + APICallMixin, + AppMixin, + SettingsMixin, + UrlsMixin, + NavigationMixin, + InvenTreePlugin, +): """Main plugin class for Shopify integration.""" - NAME = 'ShopifyPlugin' - SLUG = 'shopify' + NAME = "ShopifyPlugin" + SLUG = "shopify" TITLE = "Shopify Integration App" NAVIGATION_TAB_NAME = "Shopify" - NAVIGATION_TAB_ICON = 'fab fa-shopify' + NAVIGATION_TAB_ICON = "fab fa-shopify" - API_TOKEN = 'X-Shopify-Access-Token' - API_TOKEN_SETTING = 'API_PASSWORD' + API_TOKEN = "X-Shopify-Access-Token" # noqa: S105 + API_TOKEN_SETTING = "API_PASSWORD" # noqa: S105 - SHOPIFY_API_VERSION = '2023-04' + SHOPIFY_API_VERSION = "2023-04" @property def api_url(self): @@ -36,72 +52,93 @@ def api_url(self): def _fetch_levels(self): from .models import InventoryLevel, Variant - levels = self.api_call('inventory_levels.json', url_args={'inventory_item_ids': [a.inventory_item_id for a in Variant.objects.all()]}) - if 'errors' in levels: - raise ValueError('Errors where found', levels['errors']) + levels = self.api_call( + "inventory_levels.json", + url_args={ + "inventory_item_ids": [ + a.inventory_item_id for a in Variant.objects.all() + ] + }, + ) + if "errors" in levels: + raise ValueError("Errors where found", levels["errors"]) # create levels in db - levels = levels['inventory_levels'] + levels = levels["inventory_levels"] for level in levels: lvl, _ = InventoryLevel.objects.get_or_create( - variant=Variant.objects.get(inventory_item_id=level.get('inventory_item_id')), - location_id=level.get('location_id'), + variant=Variant.objects.get( + inventory_item_id=level.get("inventory_item_id") + ), + location_id=level.get("location_id"), defaults={ - 'available': level.get('available'), - } + "available": level.get("available"), + }, ) - lvl.updated_at = datetime.datetime.fromisoformat(level.get('updated_at')) - lvl.available = level.get('available') + lvl.updated_at = datetime.datetime.fromisoformat(level.get("updated_at")) + lvl.available = level.get("available") lvl.save() def _fetch_products(self): from .models import Product, Variant - products = self.api_call('products.json') - if 'errors' in products: - raise ValueError('Errors where found', products['errors']) + products = self.api_call("products.json") + if "errors" in products: + raise ValueError("Errors where found", products["errors"]) # create products in db - products = products['products'] + products = products["products"] for product in products: Product.objects.update_or_create( - id=product.get('id'), + id=product.get("id"), defaults={ - 'title': product.get('title'), - 'body_html': product.get('body_html'), - 'vendor': product.get('vendor'), - 'product_type': product.get('product_type'), - 'handle': product.get('handle'), - 'created_at': datetime.datetime.fromisoformat(product.get('created_at')), - 'updated_at': datetime.datetime.fromisoformat(product.get('updated_at')), - 'published_at': datetime.datetime.fromisoformat(product.get('published_at')), - } + "title": product.get("title"), + "body_html": product.get("body_html"), + "vendor": product.get("vendor"), + "product_type": product.get("product_type"), + "handle": product.get("handle"), + "created_at": datetime.datetime.fromisoformat( + product.get("created_at") + ), + "updated_at": datetime.datetime.fromisoformat( + product.get("updated_at") + ), + "published_at": datetime.datetime.fromisoformat( + product.get("published_at") + ), + }, ) # create variants in db for p in products: - for var in p['variants']: - if not Variant.objects.filter(inventory_item_id=var.get('inventory_item_id')).exists(): + for var in p["variants"]: + if not Variant.objects.filter( + inventory_item_id=var.get("inventory_item_id") + ).exists(): Variant.objects.create( - inventory_item_id=var.get('inventory_item_id'), - title=var.get('title'), - sku=var.get('sku'), - barcode=var.get('barcode'), - price=var.get('price'), - created_at=datetime.datetime.fromisoformat(var.get('created_at')), - updated_at=datetime.datetime.fromisoformat(var.get('updated_at')), - product_id=p.get('id'), + inventory_item_id=var.get("inventory_item_id"), + title=var.get("title"), + sku=var.get("sku"), + barcode=var.get("barcode"), + price=var.get("price"), + created_at=datetime.datetime.fromisoformat( + var.get("created_at") + ), + updated_at=datetime.datetime.fromisoformat( + var.get("updated_at") + ), + product_id=p.get("id"), ) # region events def process_event(self, event, *args, **kwargs): """Process triggered events.""" - if event == 'stock_stockitem.saved' and kwargs.get('model', '') == 'StockItem': + if event == "stock_stockitem.saved" and kwargs.get("model", "") == "StockItem": try: - stockitems = StockItem.objects.get(pk=kwargs.get('id')) + stockitems = StockItem.objects.get(pk=kwargs.get("id")) for level in stockitems.ShopifyInventoryLevel.all(): if level.available == stockitems.quantity: continue response = self.api_call( - endpoint='inventory_levels/set.json', + endpoint="inventory_levels/set.json", json={ "location_id": level.location_id, "inventory_item_id": level.variant.inventory_item_id, @@ -109,12 +146,13 @@ def process_event(self, event, *args, **kwargs): }, method="POST", ) - if 'inventory_level' in response: + if "inventory_level" in response: level.available = stockitems.quantity level.save() except StockItem.DoesNotExist: pass + # endregion # region views @@ -129,22 +167,27 @@ def view_index(self, request): if request.user.is_superuser: # TODO @matmair send a notification to superusers to fix setup return redirect(self.settings_url) - raise Http404(_('Plugin is not configured correctly')) + raise Http404(_("Plugin is not configured correctly")) from None context = { - 'products': Product.objects.all(), - 'levels': InventoryLevel.objects.all(), + "products": Product.objects.all(), + "levels": InventoryLevel.objects.all(), } - return render(request, 'shopify/index.html', context) + return render(request, "shopify/index.html", context) def view_increase(self, request, pk, location): """View for increasing the inventory level for an item.""" + class IncreaseForm(forms.Form): - amount = forms.IntegerField(required=True, help_text=_('New level for this level')) + amount = forms.IntegerField( + required=True, help_text=_("New level for this level") + ) - context = {'pk': pk, } + context = { + "pk": pk, + } - if request.method == 'GET': + if request.method == "GET": form = IncreaseForm() else: form = IncreaseForm(request.POST) @@ -152,48 +195,46 @@ class IncreaseForm(forms.Form): if form.is_valid(): # increase stock response = self.api_call( - endpoint='inventory_levels/set.json', + endpoint="inventory_levels/set.json", json={ "location_id": location, "inventory_item_id": pk, - "available": form.cleaned_data['amount'] + "available": form.cleaned_data["amount"], }, method="POST", ) - if 'inventory_level' in response: - return redirect(f'{self.internal_name}index') - context['error'] = _('API call was not sucessfull') + if "inventory_level" in response: + return redirect(f"{self.internal_name}index") + context["error"] = _("API call was not successful") - context['form'] = form - return render(request, 'shopify/increase.html', context) + context["form"] = form + return render(request, "shopify/increase.html", context) def view_webhooks(self, request): """View to check if the webhooks are set up correctly.""" - context = { - 'webhooks': self._webhook_check(request.get_host()) - } - return render(request, 'shopify/webhooks.html', context) + context = {"webhooks": self._webhook_check(request.get_host())} + return render(request, "shopify/webhooks.html", context) def _webhook_check(self, host): # collect current hooks target_topics = [ - 'inventory_levels/update', + "inventory_levels/update", # 'orders/updated', # 'orders/edited', ] - webhooks = self.api_call('webhooks.json') - webhooks = webhooks.get('webhooks', []) + webhooks = self.api_call("webhooks.json") + webhooks = webhooks.get("webhooks", []) # process current hooks webhooks_topics = [] webhooks_wrong_hooks = [] for item in webhooks: - if host in item.get('address', ''): - webhooks_topics.append(item.get('topic', '')) + if host in item.get("address", ""): + webhooks_topics.append(item.get("topic", "")) else: - id = item.get('id', None) - if id: - webhooks_wrong_hooks.append(id) + hook_id = item.get("id", None) + if hook_id: + webhooks_wrong_hooks.append(hook_id) changed = False # delete hooks @@ -208,70 +249,77 @@ def _webhook_check(self, host): # return all hooks if changed: - return self.api_call('webhooks.json') + return self.api_call("webhooks.json") return webhooks def _webhook_create(self, hostname, topic): from .models import ShopifyWebhook - webhook = ShopifyWebhook.objects.create(name=f'{self.slug}_{topic}') + webhook = ShopifyWebhook.objects.create(name=f"{self.slug}_{topic}") response = self.api_call( - endpoint='webhooks.json', - json={"webhook": { - "topic": topic, - "address": f'https://{hostname}/api/webhook/{webhook.endpoint_id}/', - "format": "json", - }}, - method="POST" + endpoint="webhooks.json", + json={ + "webhook": { + "topic": topic, + "address": f"https://{hostname}/api/webhook/{webhook.endpoint_id}/", + "format": "json", + } + }, + method="POST", ) - if not response.get('webhook', False): + if not response.get("webhook", False): raise KeyError(response) - webhook.shopify_webhook_id = response['webhook'].get('id', None) + webhook.shopify_webhook_id = response["webhook"].get("id", None) webhook.save() return True - def _webhook_delete(self, id): + def _webhook_delete(self, _id): self.api_call( - endpoint=f'webhooks/{id}.json', + endpoint=f"webhooks/{_id}.json", method="DELETE", ) return True + # endregion def setup_urls(self): """Returns the URLs defined by this plugin.""" return [ - url(r'increase/(?P\d+)/(?P\d+)/', self.view_increase, name='increase-level'), - url(r'webhook/', self.view_webhooks, name='webhooks'), - url(r'^', self.view_index, name='index'), + url( + r"increase/(?P\d+)/(?P\d+)/", + self.view_increase, + name="increase-level", + ), + url(r"webhook/", self.view_webhooks, name="webhooks"), + url(r"^", self.view_index, name="index"), ] SETTINGS = { - 'API_KEY': { - 'name': _('API Key'), - 'description': _('API key for your private app'), - 'default': 'a key', - 'protected': True, + "API_KEY": { + "name": _("API Key"), + "description": _("API key for your private app"), + "default": "a key", + "protected": True, }, - 'API_PASSWORD': { - 'name': _('API Passwort'), - 'description': _('API password for your private app'), - 'default': 'a password', - 'protected': True, + "API_PASSWORD": { + "name": _("API Passwort"), + "description": _("API password for your private app"), + "default": "a password", + "protected": True, }, - 'SHOP_URL': { - 'name': _('Shop url'), - 'description': _('URL for your shop instance'), - 'default': 'test.myshopify.com', + "SHOP_URL": { + "name": _("Shop url"), + "description": _("URL for your shop instance"), + "default": "test.myshopify.com", }, - 'API_SHARED_SECRET': { - 'name': _('API Shared Secret'), - 'description': _('API shared secret for your private apps webhooks'), - 'default': 'a shared key', - 'protected': True, + "API_SHARED_SECRET": { + "name": _("API Shared Secret"), + "description": _("API shared secret for your private apps webhooks"), + "default": "a shared key", + "protected": True, }, } NAVIGATION = [ - {'name': 'Product overview', 'link': 'plugin:shopify:index'}, + {"name": "Product overview", "link": "plugin:shopify:index"}, ] diff --git a/src/inventree_shopify/__init__.py b/src/inventree_shopify/__init__.py index 79e9378..532de43 100644 --- a/src/inventree_shopify/__init__.py +++ b/src/inventree_shopify/__init__.py @@ -2,4 +2,6 @@ from .ShopifyPlugin import ShopifyPlugin -__all__ = [ShopifyPlugin, ] +__all__ = [ + ShopifyPlugin, +] diff --git a/src/inventree_shopify/admin.py b/src/inventree_shopify/admin.py index 2d42246..491a8f9 100644 --- a/src/inventree_shopify/admin.py +++ b/src/inventree_shopify/admin.py @@ -1,16 +1,22 @@ -# -*- coding: utf-8 -*- +"""Admin interfaces for the Shopify app.""" from __future__ import unicode_literals from django.contrib import admin + from import_export.admin import ImportExportModelAdmin -from .models import Product, Variant, InventoryLevel, ShopifyWebhook +from .models import InventoryLevel, Product, ShopifyWebhook, Variant class InventoryLevelAdmin(ImportExportModelAdmin): + """Admin interface for the InventoryLevel model.""" - list_display = ('location_id', 'variant', 'available',) - list_filter = ('location_id', ) + list_display = ( + "location_id", + "variant", + "available", + ) + list_filter = ("location_id",) admin.site.register(Product, ImportExportModelAdmin) diff --git a/src/inventree_shopify/models.py b/src/inventree_shopify/models.py index 2a2065a..08071a4 100644 --- a/src/inventree_shopify/models.py +++ b/src/inventree_shopify/models.py @@ -1,9 +1,10 @@ """Models for ShopifyPlugin.""" import json -from common.models import VerificationMethod, WebhookEndpoint, WebhookMessage from django.db import models from django.utils.translation import gettext_lazy as _ + +from common.models import VerificationMethod, WebhookEndpoint, WebhookMessage from InvenTree.status_codes import StockHistoryCode from .ShopifyPlugin import ShopifyPlugin @@ -12,93 +13,121 @@ class Product(models.Model): """A shopify product reference.""" - id = models.IntegerField(primary_key=True, verbose_name=_('Id')) - title = models.CharField(max_length=250, verbose_name=_('Title')) - body_html = models.CharField(max_length=250, verbose_name=_('Body HTML')) - vendor = models.CharField(max_length=250, verbose_name=_('Vendor')) - product_type = models.CharField(max_length=250, verbose_name=_('Product Type')) - handle = models.CharField(max_length=250, verbose_name=_('Handle')) - created_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) - updated_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) - published_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) + id = models.IntegerField(primary_key=True, verbose_name=_("Id")) # noqa: A003 + title = models.CharField(max_length=250, verbose_name=_("Title")) + body_html = models.CharField(max_length=250, verbose_name=_("Body HTML")) + vendor = models.CharField(max_length=250, verbose_name=_("Vendor")) + product_type = models.CharField(max_length=250, verbose_name=_("Product Type")) + handle = models.CharField(max_length=250, verbose_name=_("Handle")) + created_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) + updated_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) + published_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) + + def __str__(self): + """Get string representation of product.""" + return str(self.title) class Variant(models.Model): """A shopify product variant reference.""" - inventory_item_id = models.IntegerField(verbose_name=_('Inventory item ID'), unique=True) - title = models.CharField(max_length=250, verbose_name=_('Title')) - sku = models.CharField(max_length=250, verbose_name=_('SKU')) - barcode = models.CharField(max_length=250, verbose_name=_('Barcode')) - price = models.CharField(max_length=250, verbose_name=_('Price')) - created_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) - updated_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) + inventory_item_id = models.IntegerField( + verbose_name=_("Inventory item ID"), unique=True + ) + title = models.CharField(max_length=250, verbose_name=_("Title")) + sku = models.CharField(max_length=250, verbose_name=_("SKU")) + barcode = models.CharField(max_length=250, verbose_name=_("Barcode")) + price = models.CharField(max_length=250, verbose_name=_("Price")) + created_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) + updated_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) product = models.ForeignKey( Product, on_delete=models.CASCADE, - related_name='variants', - verbose_name=_('Product') + related_name="variants", + verbose_name=_("Product"), ) part = models.ForeignKey( - 'part.Part', + "part.Part", on_delete=models.SET_NULL, - blank=True, null=True, - related_name='ShopifyVariant', - verbose_name=_('Part') + blank=True, + null=True, + related_name="ShopifyVariant", + verbose_name=_("Part"), ) + def __str__(self) -> str: + """Get string representation of variant.""" + return str(self.title) + class InventoryLevel(models.Model): """A shopify inventory level reference.""" - class Meta: - """Meta options for model.""" - - unique_together = ('location_id', 'variant', ) - - available = models.IntegerField(verbose_name=_('Available')) - location_id = models.IntegerField(verbose_name=_('Location ID')) - updated_at = models.DateField(blank=True, null=True, verbose_name=_('Creation Date')) + available = models.IntegerField(verbose_name=_("Available")) + location_id = models.IntegerField(verbose_name=_("Location ID")) + updated_at = models.DateField( + blank=True, null=True, verbose_name=_("Creation Date") + ) variant = models.ForeignKey( Variant, on_delete=models.CASCADE, blank=True, null=True, - related_name='levels', - verbose_name=_('Variant'), + related_name="levels", + verbose_name=_("Variant"), ) stock_item = models.ForeignKey( - 'stock.StockItem', + "stock.StockItem", on_delete=models.SET_NULL, - blank=True, null=True, - related_name='ShopifyInventoryLevel', - verbose_name=_('StockItem') + blank=True, + null=True, + related_name="ShopifyInventoryLevel", + verbose_name=_("StockItem"), ) + class Meta: + """Meta options for model.""" + + unique_together = ( + "location_id", + "variant", + ) + + def __str__(self) -> str: + """Get string representation of inventory level.""" + return str(self.variant) + class ShopifyWebhook(WebhookEndpoint): """Reference for Shopify specific webhook.""" - TOKEN_NAME = "X-Shopify-Hmac-Sha256" + TOKEN_NAME = "X-Shopify-Hmac-Sha256" # noqa: S105 VERIFICATION_METHOD = VerificationMethod.HMAC - shopify_webhook_id = models.IntegerField( - blank=True, - null=True - ) + shopify_webhook_id = models.IntegerField(blank=True, null=True) def init(self, request, *args, **kwargs): """Setup for webhook handler.""" super().init(request, *args, **kwargs) - self.secret = ShopifyPlugin().get_setting('API_SHARED_SECRET') + self.secret = ShopifyPlugin().get_setting("API_SHARED_SECRET") def process_payload(self, message, payload=None, headers=None): """Process a webhook message.""" - topic = headers['X-Shopify-Topic'] + topic = headers["X-Shopify-Topic"] if self.check_if_handled(headers): return False - if topic == 'inventory_levels/update': + if topic == "inventory_levels/update": # handle invetorylevel update update_inventory_levels(payload) # elif topic == 'orders/edited': @@ -120,13 +149,15 @@ def check_if_handled(self, headers: dict) -> bool: Returns: bool: True if message handled """ - message_id = headers['X-Shopify-Webhook-Id'] - msgs = WebhookMessage.objects.filter(endpoint=self, header__contains=message_id, worked_on=True) + message_id = headers["X-Shopify-Webhook-Id"] + msgs = WebhookMessage.objects.filter( + endpoint=self, header__contains=message_id, worked_on=True + ) if msgs.exists(): # this was already worked on - we think msg = msgs.first() payload = json.loads(msg.header) - if payload.get('X-Shopify-Webhook-Id', '') == message_id: + if payload.get("X-Shopify-Webhook-Id", "") == message_id: # now we can be sure return True return False @@ -143,10 +174,13 @@ def update_inventory_levels(payload: dict): :type payload: dict """ # fetch item - item = InventoryLevel.objects.filter(variant__inventory_item_id=payload['inventory_item_id'], location_id=payload['location_id']) + item = InventoryLevel.objects.filter( + variant__inventory_item_id=payload["inventory_item_id"], + location_id=payload["location_id"], + ) if item.exists() and item.count() == 1: item = item.first() - avail = payload['available'] + avail = payload["available"] # set item qty item.available = avail @@ -162,9 +196,9 @@ def update_inventory_levels(payload: dict): item.stock_item.add_tracking_entry( StockHistoryCode.STOCK_COUNT, None, - notes='changed in shopify inventory', + notes="changed in shopify inventory", deltas={ - 'quantity': float(avail), - } + "quantity": float(avail), + }, ) item.save() diff --git a/src/inventree_shopify/templates/shopify/increase.html b/src/inventree_shopify/templates/shopify/increase.html index d88c3e2..73dcda8 100644 --- a/src/inventree_shopify/templates/shopify/increase.html +++ b/src/inventree_shopify/templates/shopify/increase.html @@ -11,4 +11,4 @@

Change level for {{pk}}

{{ form }} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/inventree_shopify/templates/shopify/webhooks.html b/src/inventree_shopify/templates/shopify/webhooks.html index e0570f2..000e6d9 100644 --- a/src/inventree_shopify/templates/shopify/webhooks.html +++ b/src/inventree_shopify/templates/shopify/webhooks.html @@ -13,4 +13,4 @@

{% blocktrans with lgt=webhooks|length %}{{lgt}} Webhooks:{% endblocktrans % {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %}