Skip to main content

ERPDesktop plugin SDK — build, package, and publish plugins for the ERPDesktop marketplace

Project description

ERPDesktop Plugin SDK

Build, package, and publish plugins for the ERPDesktop marketplace. ERPDesktop plugins run as Python sidecars alongside the desktop app and communicate with ERPNext/Odoo over a secure JSON-RPC channel.

Install

pip install erpdesktop

Requires Python 3.11+.


Quick start

# Scaffold a new plugin
erpdesktop init com.yourcompany.myplugin

# Validate plugin.json
erpdesktop validate

# Package into a distributable .zip
erpdesktop package

# Publish to the marketplace
erpdesktop publish --changelog "Initial release"

Plugin lifecycle

from erpdesktop import PluginBase, log, event

class MyPlugin(PluginBase):
    plugin_id = "com.example.myplugin"
    plugin_version = "1.0.0"

    def on_start(self, config: dict) -> None:
        """Called when the plugin is started. Register your commands here."""
        log("info", "Plugin started", branch=config.get("branch"))
        self.register_command("ping", self.handle_ping)

    def on_stop(self) -> None:
        """Called on graceful shutdown."""
        log("info", "Plugin stopping")

    def on_config_change(self, config: dict) -> None:
        """Called when the user updates plugin settings."""
        pass

    def handle_ping(self, params: dict) -> dict:
        return {"pong": True}

if __name__ == "__main__":
    MyPlugin().run()

Modules

log — structured logging

from erpdesktop import log

log("info", "Sync complete", records=47, duration_ms=320)
log("error", "Device unreachable", device_id="K50-001")
# levels: debug | info | warn | error | critical

event — telemetry

Emit custom analytics events visible in the Publisher Dashboard. Do not include personal data or ERP record contents.

from erpdesktop import event

event("sync_completed", records=47, duration_ms=320)
event("device_connected", model="K50", firmware="2.1.3")

erpnext_request / odoo_request — ERP API

Call your connected ERPNext or Odoo site. Requires network.outbound.erpnext permission.

from erpdesktop import erpnext_request, odoo_request

# ERPNext
result = erpnext_request("GET", "/api/resource/Employee", params={"limit": 10})

# Odoo
result = odoo_request("POST", "/web/dataset/call_kw", json={...})

http — external HTTP

Make calls to third-party APIs. Requires network.outbound.internet permission and the endpoint declared in network_endpoints in your plugin.json.

from erpdesktop import http

resp = http.get("https://api.example.com/data", params={"key": "val"})
resp = http.post("https://api.example.com/sync", json={"records": [...]})

storage — persistent key-value store

from erpdesktop import storage

storage.set("last_sync", "2024-01-15T10:30:00")
value = storage.get("last_sync")          # returns str or None
value = storage.get("last_sync", "never") # with fallback

secrets — encrypted credential storage

Store API keys and passwords in the OS keychain — never in config files.

from erpdesktop import secrets

secrets.set("api_key", "sk-abc123")
key = secrets.get("api_key")          # returns str or None
key = secrets.get("api_key", "")      # with fallback

permissions — runtime permission checks

from erpdesktop import permissions

# Check before doing something sensitive
if permissions.require("network.outbound.internet"):
    result = http.get("https://api.example.com/data")

# Raise PermissionDenied automatically if not granted
permissions.enforce("network.outbound.erpnext")

# Check multiple
if permissions.has_all("network.outbound.erpnext", "storage.read"):
    ...

if permissions.has_any("network.outbound.erpnext", "network.outbound.internet"):
    ...

device — biometric/access device leasing

Claim exclusive access to a physical device (e.g. fingerprint reader) so two plugins don't conflict.

from erpdesktop import device

if device.lease("ZK-192.168.1.10", metadata={"model": "K50"}):
    # we have exclusive access
    ...
    device.release("ZK-192.168.1.10")

device.renew("ZK-192.168.1.10")            # extend lease
owner = device.is_leased("ZK-192.168.1.10") # returns plugin_id or None

notify — desktop notifications

from erpdesktop import notify

notify.send("Sync complete", "47 attendance records pushed to ERPNext")
notify.send("Device offline", "ZK-K50 not reachable", urgent=True)

i18n — translations

from erpdesktop import i18n

i18n.set_locale("ne")                              # Nepali
label = i18n.t("sync.button", fallback="Sync")
label = i18n.t("greeting", fallback="Hello {name}", name="Ram")

Place translation files at locales/<lang>.json in your plugin directory:

{ "sync.button": "सिंक गर्नुहोस्", "greeting": "नमस्ते {name}" }

plugin.json reference

{
  "id": "com.yourcompany.myplugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "What your plugin does.",
  "author": "Your Company",
  "plugin_type": "commercial",
  "entry": "main.py",
  "permissions": [
    "network.outbound.erpnext",
    "network.outbound.internet",
    "storage.read",
    "storage.write"
  ],
  "network_endpoints": [
    {
      "url": "https://api.example.com",
      "purpose": "Sync attendance records",
      "data_sent": "Employee IDs, timestamps"
    }
  ],
  "config_schema": {
    "branch": { "type": "string", "label": "Branch", "required": true }
  }
}

plugin_typeopen_source (requires source_url), commercial, or private

network_endpoints — required when network.outbound.internet is declared. Shown to the user at install time. The SDK enforces the allowlist at runtime.


CLI reference

Command Description
erpdesktop login Authenticate with the ERPDesktop marketplace
erpdesktop logout Sign out
erpdesktop whoami Show current logged-in publisher
erpdesktop init <id> Scaffold a new plugin
erpdesktop validate Validate plugin.json against the schema
erpdesktop package Build a .zip for distribution
erpdesktop publish Publish to the marketplace
erpdesktop logs Stream live plugin logs
erpdesktop dev Start plugin in dev/watch mode
erpdesktop --version Show SDK version

Publishing checklist

  1. plugin_type is set correctly
  2. All permissions used in code are declared in plugin.json
  3. Every external URL in http.* calls is listed in network_endpoints
  4. No ERP record data passed to event()
  5. erpdesktop validate passes with no errors
  6. erpdesktop package produces a valid .zip
  7. erpdesktop publish --changelog "What changed"

License

MIT © BatchNepal

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

erpdesktop-1.0.1.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

erpdesktop-1.0.1-py3-none-any.whl (32.5 kB view details)

Uploaded Python 3

File details

Details for the file erpdesktop-1.0.1.tar.gz.

File metadata

  • Download URL: erpdesktop-1.0.1.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.10

File hashes

Hashes for erpdesktop-1.0.1.tar.gz
Algorithm Hash digest
SHA256 94f38a9fc7fc19bfe18cf564df21f35088a6c7eca536619a39a8aff3a8cf829a
MD5 49a3a495369a7382e1808a15bc0abed7
BLAKE2b-256 17a3f33854d3a5300f666fb6a5d5d25a56387f8ec544bbd4e0652030d8f8186a

See more details on using hashes here.

File details

Details for the file erpdesktop-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: erpdesktop-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 32.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.10

File hashes

Hashes for erpdesktop-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6012b31257f860ee48a6f882b4cb86a7a89de446437223b7718735bbd456f86e
MD5 ca1949221b183d6375effef549ee934d
BLAKE2b-256 e2367dca37448f4982a5a760cc79747bed8be1c3df6b3d92aa01754137099ec4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page