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.4.tar.gz (39.0 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.4-py3-none-any.whl (41.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for erpdesktop-1.0.4.tar.gz
Algorithm Hash digest
SHA256 16ab89b13ec4cd30833e12220e3f2f05be14fcb44327f59ec173fe9090c63140
MD5 ca53b7ae5ae75e024d90cd7cfbc265bf
BLAKE2b-256 e47b0616e80c734d02b99fe3001d4c79926a6bbbe551044049d0d5a8a7dc0510

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for erpdesktop-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 754aefe36ba82691c999658ae4dad4d3fa194f14b9342458462ca1babb89e801
MD5 22261ad26bb17c08d3561eec20078eb2
BLAKE2b-256 ea2f96460b748e578f15d572f8107f958be28581c1622c6138be70961c61cd68

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