UI/settings api mcp (#524)

* feat(voucher): add create voucher and correct entry previews; update commit methods

* feat: add support for pending operations in API key scopes and OAuth client management

- Introduced new API key scopes for reading and approving pending operations.
- Updated the scope groups to include pending operations.
- Added new tools for listing and managing pending operations.
- Implemented OAuth client registration and revocation endpoints.
- Created a UI panel for managing OAuth clients, including registration and revocation.
- Added tests for pending operations tools and OAuth allowlist functionality.
- Implemented a database migration for OAuth client registrations with appropriate policies and constraints.

* feat: Implement OAuth client registration rate limiting and enhance security measures

- Added IP-based rate limiting to the OAuth client registration endpoint to prevent enumeration attacks.
- Introduced a service-role client for allowlist lookups, ensuring trust boundaries are maintained.
- Updated error responses to be uniform across different types of redirect URI validation failures.
- Enhanced tests to reflect changes in OAuth scope handling, ensuring fallback to read-only scopes when no scopes are provided.
- Improved handling of high-risk pending operations, requiring explicit confirmation for approvals.
- Added audit logging for OAuth client revocations and pending operation approvals/rejections to maintain a security audit trail.
- Refactored API key scope management to include default read-only scopes for OAuth-issued keys and added segregation-of-duties checks.

* feat: add recurring invoice scheduling functionality

- Implemented recurring invoice schedules with a new database schema.
- Created API routes for managing recurring invoices (GET and POST).
- Added cron job to automatically generate invoices based on schedules.
- Developed service functions for computing next run dates and executing schedules.
- Added tests for the new functionality, including validation and success cases.
- Introduced error handling for various scenarios in the invoice creation process.

* feat: refine VAT rate validation and enhance recurring invoice handling
This commit is contained in:
Mattsson
2026-05-19 13:48:32 +02:00
committed by GitHub
parent 16164ea14c
commit e211ab31be
22 changed files with 2133 additions and 76 deletions
+50
View File
@@ -177,6 +177,56 @@ export const CreateCreditNoteSchema = z.object({
reason: z.string().optional(),
})
// ============================================================
// Recurring invoice schedule schemas
// ============================================================
// Swedish VAT rates per ML 17 kap 24§ p.9 — null means "use customer default
// from getAvailableVatRates". Any other value would produce a non-compliant
// invoice (buyer cannot deduct ingående moms). Cron-time validation against
// the customer's allowed set still runs in executeRecurringSchedule.
export const RecurringScheduleItemSchema = z.object({
description: z.string().min(1, 'Item description is required'),
quantity: z.number().positive('Quantity must be positive'),
unit: z.string().min(1, 'Unit is required').default('st'),
unit_price: z.number(),
vat_rate: z
.union([z.literal(0), z.literal(6), z.literal(12), z.literal(25)])
.nullable()
.optional(),
})
export const CreateRecurringScheduleSchema = z.object({
customer_id: uuid,
name: z.string().min(1, 'Schedule name is required').max(200),
day_of_month: z.number().int().min(1).max(31),
payment_terms_days: z.number().int().min(0).max(90).default(30),
currency: CurrencySchema.default('SEK'),
your_reference: z.string().optional(),
our_reference: z.string().optional(),
notes: z.string().optional(),
auto_send: z.boolean().default(false),
// Optional: when to first run. Defaults to next occurrence of day_of_month
// (today if day_of_month === today, otherwise next month).
start_date: isoDate.optional(),
items: z.array(RecurringScheduleItemSchema).min(1, 'At least one item is required'),
})
export const UpdateRecurringScheduleSchema = z.object({
customer_id: uuid.optional(),
name: z.string().min(1).max(200).optional(),
day_of_month: z.number().int().min(1).max(31).optional(),
payment_terms_days: z.number().int().min(0).max(90).optional(),
currency: CurrencySchema.optional(),
your_reference: z.string().nullable().optional(),
our_reference: z.string().nullable().optional(),
notes: z.string().nullable().optional(),
auto_send: z.boolean().optional(),
status: z.enum(['active', 'paused']).optional(),
// Replace all items if provided. Omit to keep existing items unchanged.
items: z.array(RecurringScheduleItemSchema).min(1).optional(),
})
export const MarkInvoicePaidSchema = z.object({
payment_date: isoDate.optional(),
exchange_rate_difference: z.number().optional(),