Pricing: migrate from app_config JSON to relational tables
Replaces the two `pricing_*_tiers_json` blobs and five `first_session_discount_*` keys in app_config with dedicated `pricing_tiers` and `pricing_promotions` tables plus matching `_history` audit tables. UUID PKs, UNIQUE(mode, minutes) natural-key constraint, optimistic-lock via `updated_at` token returning 409 STALE_WRITE on conflicts. Every mutation writes a history row capturing the operator (changed_by from request.auth.userId) and change_kind. CC SettingsPage replaces the JSON-textarea editors with per-row tables — add / edit / soft-delete / reactivate / reorder, plus a buffered first-session discount form with the same optimistic-lock contract. `minutes` and `mode` are read-only on edit since they form the natural key; operators soft-delete and recreate to change duration. Stage 5 fixes a latent leak: `client.payment.routes.js` had its own local `readDiscountConfig` that still read from app_config — would have silently fallen to hardcoded defaults once the legacy rows were deleted. Now reads from pricing_promotions via the shared service helper, so CC edits to the first- session discount affect actual payment pricing on the next request. Customer-facing GET /api/client/chat/pricing shape unchanged (id values are now UUIDs instead of "5"/"12"/"60" but lookups happen by (mode, minutes), so no app changes needed). 27 new backend tests, all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,3 +44,16 @@ export const ExtensionTimeoutAction = Object.freeze({
|
||||
AUTO_APPROVE: 'auto_approve',
|
||||
AUTO_REJECT: 'auto_reject',
|
||||
})
|
||||
|
||||
// Session / pricing modes. Mirrors backend `SessionMode` enum.
|
||||
export const SessionMode = Object.freeze({
|
||||
CHAT: 'chat',
|
||||
CALL: 'call',
|
||||
})
|
||||
|
||||
// Mirror of backend error codes returned on optimistic-lock conflict / validation.
|
||||
export const ApiErrorCode = Object.freeze({
|
||||
STALE_WRITE: 'STALE_WRITE',
|
||||
VALIDATION: 'VALIDATION',
|
||||
NOT_FOUND: 'NOT_FOUND',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user