Skip to content

Release/2.0.0 - #1

Merged
Kamaar merged 3 commits into
mainfrom
release/2.0.0
Sep 2, 2026
Merged

Kamaar merged 3 commits into
mainfrom
release/2.0.0

Conversation

@Kamaar

@Kamaar Kamaar commented Sep 2, 2026

Copy link
Copy Markdown
Owner

No description provided.

Kamaar and others added 3 commits September 2, 2026 11:13
Rebuild the plugin on the official plugin creator template (creator 1.20.0),
targeting InvenTree 1.0+ (developed against 1.5.2). The plugin slug and the
distribution name are unchanged, so stored settings survive and installing the
new distribution is an upgrade rather than a new package.

Fix three bugs that made the 1.x settings silently inert:

- The generate_batch_code hook read kwargs['stock_item'], but InvenTree passes
  the stock item as 'item' (see stock/generators.py and the caller kwargs in
  GenerateBatchCodeSerializer). Part and location were therefore always None,
  so PER_PART, PER_LOCATION and USE_LOCATION_PREFIX never had any effect.
- get_setting()'s second positional parameter is 'cache', not a default value,
  so every call of the form get_setting('KEY', default) passed the default as
  the cache flag.
- MIN_DIGITS was only applied on the format-error fallback path, never to
  normal output.

Replace the derived counter with a persistent one. models.BatchCounter holds
one row per scope, advanced atomically under select_for_update(), so the
sequence no longer depends on the code format being sortable as a string and
survives concurrent stock creation. SEED_FROM_EXISTING raises the counter past
numbers already present in existing batch codes, so upgrading from 1.x does not
reissue codes which are already in use. This requires AppMixin, so the plugin
is now a Django app: it needs a server restart to load, and a migration.

Other changes:

- DAILY_RESET now resets the counter itself, rather than filtering existing
  codes for today's date, so it no longer requires {date} in CODE_FORMAT.
- TRIGGER_MODE=on_receive now means "a purchase order is part of the request",
  which is what the hook context actually exposes.
- Remove TARGET_FIELD: the hook returns a string and InvenTree decides where it
  goes, which is always StockItem.batch.
- Add preview/ and generate/ REST endpoints via UrlsMixin. The 1.x urls.py was
  never mounted (no UrlsMixin, no setup_urls) and called a method that did not
  exist, so nothing depended on it.
- Add a React panel for the stock item page and a live format preview on the
  plugin settings page, with a complete Italian catalog.
- Manage the Python environment with uv; format and lint with ruff; run CI on
  GitHub Actions.
- Version is now declared once, in batchcode_plugin/__init__.py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cover the parts of the plugin that decide what a batch code looks like:
format rendering and padding, counter scoping, the generate_batch_code hook
context, trigger modes, prefix resolution, role gating, the panel context and
serializer construction. 76 tests, no InvenTree checkout required.

tests/conftest.py configures Django minimally, stubs the InvenTree modules and
loads the plugin modules by path, then subclasses BatchCodePlugin with a
dict-backed get_setting. Only persistence is faked: build_key is delegated to
the real BatchCounter, so the scope key under test is the production one.

Fix a bug the suite found immediately: the serializers declared
PrimaryKeyRelatedField(queryset=None) and filled the queryset in from
Serializer.__init__, but DRF validates queryset inside the *field*
constructor, which runs when the class body is evaluated. serializers.py
therefore raised AssertionError on import, which would have taken down the
plugin's whole URL set via setup_urls -> views -> serializers. Replaced with
LazyModelField subclasses that override get_queryset(), deferring the model
import and suppressing DRF's constructor check.

Drop .github/workflows/pypi.yaml: the plugin is not published to PyPI, and the
workflow referenced a PYPI_API_TOKEN secret that does not exist, so the first
published release would have failed. README documents how to restore it. The
install instructions now point at this repository rather than PyPI, and note
that a git install omits the uncommitted frontend bundles.

Pin the development interpreter with .python-version, and mark the Django and
DRF dev dependencies for python_version >= 3.10: the distribution still
declares requires-python >=3.9, matching what InvenTree itself claims, while
Django 5.2 needs 3.10+.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
InvenTree's plugin installer (plugin/installer.py) only accepts VCS URLs,
composed as {packagename}@{url}; a plain https:// URL is handed to pip as a
package *index* (-i), not as a package to install, so a link to a release file
does not work from that form. A VCS install builds from source, which means a
plugin installed through Settings -> Plugins had no user interface at all,
because batchcode_plugin/static/ was gitignored.

Commit the bundles, and guard them against going stale: the CI frontend job
rebuilds both the bundles and the message catalogs and fails if the result
differs from what is committed. It stages before diffing, since bundle
filenames carry a content hash and a plain `git diff` would not see the new
files. Switch CI to `npm ci` - several dependencies are declared as "latest",
so only the lockfile makes the output reproducible enough to compare. Fold the
translation check into the same job and drop translations.yaml, which was
repeating the same npm install and build for one diff.

Add .gitattributes pinning line endings to LF. This is load-bearing rather
than cosmetic: a sourcemap embeds its sources verbatim in "sourcesContent",
line endings included, so a CRLF checkout of frontend/src builds different
.js.map files and would fail the artifact check on Windows with no real
change. Generated artifacts are marked -text so they stay byte-identical.

Document the install procedure properly, which the previous version got wrong
in two ways:

- Three global settings gate the mixins this plugin uses, and all three
  default to False: ENABLE_PLUGINS_APP (AppMixin), ENABLE_PLUGINS_URL
  (UrlsMixin) and ENABLE_PLUGINS_INTERFACE (UserInterfaceMixin). They live in
  common/setting/system.py as database settings, not in config.yaml, so
  searching the config template for them finds nothing. CLAUDE.md previously
  claimed no such flag existed.
- Neither the installer nor the container entrypoint runs migrations, so
  installation cannot be completed from the web interface alone: an admin must
  run `invoke update` (or `invoke migrate`) once. The README now says so up
  front and walks through the four steps in order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Kamaar
Kamaar merged commit 67c1c9b into main Sep 2, 2026
6 checks passed
@Kamaar
Kamaar deleted the release/2.0.0 branch September 2, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant