Ninth plugin out, third resurrection: switched off since 2026-08-13 with the permission reduced to api: [], so the server booted clean and every panel request 404'd. invoiceshelf/schema was also commented out of the drizzle barrel. Named `invoices`, not `invoiceshelf`. The appName drives the API prefix, the frontend route, the table prefix and the install key all at once, and before extraction those had drifted: the API was /api/invoiceshelf while the screen was /invoices. The URL is what a person sees and should name what the plugin shows rather than the daemon behind it — the permission key and label were already `invoices`, so the API prefix was the odd one out. Cost: three call sites, and renaming invoiceshelf_accounts to invoices_accounts to satisfy the table-prefix rule. That rename was free ONLY because the table existed on no server, which was checked before deciding. With rows in it, keeping the vendor name would have been the better trade: db:push sees a rename as a drop plus a create and this project has no migration mechanism. The sidecar keeps its Invoiceshelf-named functions deliberately — they name the upstream API it actually speaks. 91 palette classes converted, 52 of them in STATUS_TONE, which was already the single source of status colour but held raw palette strings. SENT and VIEWED now share `info`: the closed six-tone set has no second shade of "in flight, not yet actioned". Recorded in PLUGIN.md as a real loss.
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
/**
|
|
* Invoices — your InvoiceShelf books, inside Officer.
|
|
*
|
|
* ── Why `invoices` and not `invoiceshelf` ──
|
|
*
|
|
* The appName is THE id: it drives the API prefix, the frontend route, the table prefix, the sidecar
|
|
* suffix and the install key, all at once. Before extraction those had drifted apart — the API was
|
|
* `/api/invoiceshelf` while the screen was `/invoices` — and a plugin cannot hold both.
|
|
*
|
|
* Named for what it shows rather than the daemon behind it, so the URL a person sees stays `/invoices`.
|
|
* The cost was renaming `invoiceshelf_accounts` to `invoices_accounts` to satisfy the table-prefix rule,
|
|
* which was free here because the table existed on no server. Had there been rows, keeping the vendor
|
|
* name would have been the better trade.
|
|
*
|
|
* ── What this plugin is, and what it is not ──
|
|
*
|
|
* It does not install or supervise InvoiceShelf, and it holds no books. You point it at an instance and
|
|
* it speaks that API on your behalf, holding the credentials server-side.
|
|
*
|
|
* ── Where the connection lives ──
|
|
*
|
|
* In this plugin's own `invoices_accounts` — a registry, like photos and jellyfin, because one person
|
|
* really does keep more than one company's books. Label, URL, encrypted credential, one active.
|
|
*
|
|
* ── No `osDependencies` ──
|
|
*
|
|
* Nothing shells out. The sidecar speaks InvoiceShelf's HTTP API over `fetch`.
|
|
*/
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '1.0.0',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Invoices',
|
|
summary: 'InvoiceShelf books — invoices, estimates, customers and payments',
|
|
icon: 'ReceiptText',
|
|
color: '#0ea5e9',
|
|
|
|
permissions: [
|
|
{
|
|
key: 'invoices',
|
|
label: 'Invoices',
|
|
description: 'InvoiceShelf books',
|
|
},
|
|
],
|
|
};
|