This is Craftivus's own internal system — a separate Frappe/ERPNext site, entirely distinct from any customer's site, used only by Craftivus staff. See Architecture for what it's for.
Same bench-level prerequisites as Installation: Backend. This is a genuinely separate site — it does not live inside any customer's ERPNext installation.
bench new-site subscription.craftivus.internal \
--mariadb-root-password '<root-password>' \
--admin-password '<pick-a-real-password>' \
--mariadb-user-host-login-scope='%'
bench --site subscription.craftivus.internal install-app erpnext
Required because craftivus_subscription deliberately reuses ERPNext's native Subscription and Subscription Plan doctypes rather than reinventing recurring billing — see Developer: Subscription App for why. As with any fresh ERPNext install, you need at least a minimal root taxonomy (Company, Territory, Customer Group, Item Group, UOM) before Customer/Item/Subscription Plan records can be created — see the troubleshooting table on the Backend installation page if you hit LinkValidationError here.
bench --site subscription.craftivus.internal install-app craftivus_subscription
bench --site subscription.craftivus.internal migrate
Migration applies the service_type split and its conditional fields on Subscription Plan, the hosting/integration fields on Customer, the revoke fields on Subscription, and creates the Staff Action Log doctype.
If you intend to use the "fix a wrong payment method" correction flow anywhere in the platform, every Mode of Payment you use needs a default account configured for the relevant company (Desk → Mode of Payment → Accounts child table). This isn't specific to craftivus_subscription — it's a standard ERPNext requirement — but it's easy to miss since Cash typically already has one seeded and a second method (Card, Cheque, etc.) usually doesn't.
Unlike every customer-facing app, Craftivus staff use this site through ordinary Frappe Desk — real System User accounts, real Roles. ADR 0001's "no Desk access" rule is specifically about customers; it was never a rule about Craftivus's own staff. There is no separate frontend to build or deploy for this app — Frappe's own list views, form views, and the custom Change Plan / Revoke buttons (added via a real public/js/subscription.js file + the doctype_js hook, not a runtime-created Client Script) are the entire UI.
Creating a Customer record on craftivus_subscription automatically generates that customer's integration credentials (push_key, heartbeat_key for POS; admin_api_key/admin_api_secret for ERPNext Only) via a before_insert hook. But the customer's own site doesn't know these values yet — that's a manual, one-time pairing step during onboarding:
Customer on craftivus_subscription. Note the generated push_key.Craftivus Subscription State singleton (Desk) and paste that push_key into its Expected Push Key field. This is what makes receive_state/revoke_all_devices calls from craftivus_subscription trustable — an unset value rejects every push by default.Subscription on craftivus_subscription linking that Customer to a Subscription Plan. Saving it triggers the first push automatically.bootstrap() against the customer's site (with real device credentials) and check the subscription block reflects the plan you just assigned.There is currently no UI for step 2 — it's a Desk field edit. Automating this handshake is a natural next step once onboarding volume justifies it.
| Symptom | Cause | Fix |
|---|---|---|
A push from craftivus_subscription to a customer's site fails with 401 Unauthorized |
The integration used Authorization: token ... instead of the custom header |
Craftivus's push contract deliberately uses X-Craftivus-Push-Key, not Authorization — Frappe's own auth middleware intercepts and rejects the token scheme for anything that isn't a real User's api_key:api_secret pair before your own code ever runs |
A push succeeds locally (direct Python call) but fails as 417 or similar over real HTTP with a validation error about a device |
revoke_all_devices swept up a never-activated Pending device (no device_id yet), which trips POS Device's own validation |
Filter to status in (Active, Suspended) before revoking in bulk |
Change Plan/Revoke buttons don't appear on the Subscription form |
Asset build didn't pick up the new public/js/subscription.js, or cache is stale |
bench build --app craftivus_subscription then bench --site <site> clear-cache |
frappe.exceptions.PermissionError from inside make_return_doc, .cancel(), or .submit() when this app calls into a customer's craftivus_core API on their behalf |
Same session-permission-check issue described on the Architecture page — but note this happens on the customer's craftivus_core site, not here |
Fix belongs in craftivus_core, not in craftivus_subscription |