Show staff-only products in the order catalog for staff

POST /orders already lets staff order products that customers cannot
order themselves, but the catalog hid them, so e.g. KeyHelp Unlimited
could not be booked for a customer. Staff now see all active products,
marked as staff-only where applicable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kundencenter 2026-10-01 12:24:48 +02:00
parent 466bd83b7e
commit c276eb8ceb
2 changed files with 7 additions and 5 deletions

View file

@ -11,7 +11,7 @@ import { one, query, run, tx } from '../../core/db.js';
import { audit } from '../../core/audit.js';
import { clientIp, requireAuth, requirePermission } from '../../core/auth.js';
import { AppError, badRequest, conflict, notFound } from '../../core/errors.js';
import { canInOrg } from '../../core/policy.js';
import { can, canInOrg } from '../../core/policy.js';
import type { KcModule } from '../../core/module.js';
const int = (max: number) => z.number().int().min(0).max(max);
@ -272,7 +272,9 @@ export const catalogModule: KcModule = {
const q = z.object({ org: z.string().uuid() }).parse(req.query);
if (!canInOrg(a.principal, q.org, 'orders.read', 'products.read')) throw notFound();
const org = await one('SELECT customer_type FROM organizations WHERE id = ?', [q.org]);
const rows = await query(`${productSelect} WHERE p.status = 'active' AND p.orderable_by_customer = 1 ORDER BY v.name`);
// Personal darf auch Produkte bestellen, die Kunden nicht selbst bestellen können (siehe POST /orders)
const staff = can(a.principal, 'orders.write');
const rows = await query(`${productSelect} WHERE p.status = 'active'${staff ? '' : ' AND p.orderable_by_customer = 1'} ORDER BY v.name`);
return Promise.all(rows.map(async (r) => {
const price = calculatePrice({ basis: r.price_basis, setupCents: r.setup_cents, recurringCents: r.recurring_cents, taxBp: r.tax_bp, interval: r.billing_interval, quantity: 1, discountBp: 0 });
// Staffelpreise nach Laufzeit (optional): je gewählter Laufzeit ein eigener Gesamtpreis statt des Basispreises.
@ -280,7 +282,7 @@ export const catalogModule: KcModule = {
termMonths: t.term_months,
price: calculatePrice({ basis: r.price_basis, setupCents: 0, recurringCents: t.recurring_cents, taxBp: r.tax_bp, interval: r.billing_interval, quantity: 1, discountBp: 0 }).recurring,
}));
return { id: r.id, sku: r.sku, category: r.category, name: r.vname, description: r.description, requiresApproval: !!r.requires_approval, customerType: org?.customer_type, price, termMonths: r.term_months, renewal: r.renewal, renewalTermMonths: r.renewal_term_months, noticeDays: r.notice_days, termPrices: tiers };
return { id: r.id, sku: r.sku, category: r.category, name: r.vname, description: r.description, requiresApproval: !!r.requires_approval, orderableByCustomer: !!r.orderable_by_customer, customerType: org?.customer_type, price, termMonths: r.term_months, renewal: r.renewal, renewalTermMonths: r.renewal_term_months, noticeDays: r.notice_days, termPrices: tiers };
}));
});
},