diff --git a/openapi.yaml b/openapi.yaml index 249d8fbb..66b16985 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -38,6 +38,8 @@ tags: description: User and employee authentication endpoints - name: Users description: User management and customer operations + - name: Search + description: System-wide search endpoints - name: Orders description: Order creation, management, and retrieval - name: Order Items @@ -1517,6 +1519,11 @@ paths: summary: Generate 2FA secret description: Generate a new TOTP secret for the authenticated user/subuser operationId: setup2fa + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: 2FA secret generated successfully @@ -1555,6 +1562,9 @@ paths: responses: '200': description: 2FA enabled successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '401': @@ -1581,6 +1591,9 @@ paths: responses: '200': description: 2FA disabled successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '401': @@ -1729,6 +1742,9 @@ paths: responses: '201': description: Customer registered successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -1799,6 +1815,10 @@ paths: type: integer '404': description: Invalid or expired token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /auth/password-reset/set: post: @@ -1842,6 +1862,10 @@ paths: $ref: '#/components/responses/BadRequest' '404': description: Invalid or expired token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /su/intimidate: post: @@ -1932,6 +1956,9 @@ paths: responses: '200': description: User updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '401': @@ -1997,6 +2024,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Users @@ -2015,6 +2045,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /customer/department/default: get: @@ -2029,6 +2062,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Users @@ -2047,6 +2083,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} delete: tags: - Users @@ -2059,6 +2098,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /customer/pricing/fixed: get: @@ -2074,6 +2116,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Users @@ -2093,6 +2138,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} delete: tags: - Users @@ -2106,6 +2154,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /account/notifications: put: @@ -2126,6 +2177,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /user/permissions: get: @@ -2136,6 +2190,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /customers: get: @@ -2147,9 +2204,19 @@ paths: - $ref: '#/components/parameters/PageParam' - $ref: '#/components/parameters/PerPageParam' - $ref: '#/components/parameters/SearchParam' + - name: barred + in: query + required: false + description: Optional e-conomic barred customer filter. + schema: + type: string + enum: ['true', 'false', 'barred', 'active', '1', '0'] responses: '200': description: Success + content: + application/json: + schema: {} /superuser/user/discounts: get: @@ -2165,6 +2232,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Users @@ -2185,6 +2255,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/user/keys: get: @@ -2203,6 +2276,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Users @@ -2222,6 +2298,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/user/password: post: @@ -2242,6 +2321,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /admin/customer/getUserId: get: @@ -2358,6 +2440,9 @@ paths: responses: '200': description: Order deleted successfully + content: + application/json: + schema: {} '401': $ref: '#/components/responses/Unauthorized' '404': @@ -2377,6 +2462,9 @@ paths: responses: '200': description: Order updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -2417,6 +2505,9 @@ paths: responses: '200': description: Order updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '401': @@ -2484,6 +2575,9 @@ paths: responses: '200': description: Order marked as completed successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -2506,6 +2600,9 @@ paths: responses: '200': description: Wash certificate generated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -2547,6 +2644,9 @@ paths: responses: '201': description: Order item added successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' put: @@ -2564,6 +2664,9 @@ paths: responses: '200': description: Order item updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' delete: @@ -2581,6 +2684,9 @@ paths: responses: '200': description: Order item deleted successfully + content: + application/json: + schema: {} '404': $ref: '#/components/responses/NotFound' @@ -2627,6 +2733,9 @@ paths: responses: '201': description: Department created successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' put: @@ -2644,6 +2753,9 @@ paths: responses: '200': description: Department updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -2663,6 +2775,9 @@ paths: responses: '200': description: Department categories retrieved successfully + content: + application/json: + schema: {} post: tags: - Departments @@ -2683,13 +2798,25 @@ paths: responses: '201': description: Category added to department successfully + content: + application/json: + schema: {} delete: tags: - Departments summary: Remove category from department operationId: removeDepartmentCategory + requestBody: + required: false + content: + application/json: + schema: {} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /departments/self-serve/enabled: get: @@ -2762,7 +2889,11 @@ paths: required: true schema: {type: integer} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /department/lanes: get: @@ -2801,6 +2932,9 @@ paths: responses: '201': description: Department lane created successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' put: @@ -2818,6 +2952,9 @@ paths: responses: '200': description: Department lane updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '404': @@ -2906,6 +3043,9 @@ paths: responses: '200': description: Department gate deleted + content: + application/json: + schema: {} /department/relays: get: @@ -2990,6 +3130,9 @@ paths: responses: '200': description: Department relay deleted + content: + application/json: + schema: {} /department/lanes/dynamic-image: get: @@ -3084,6 +3227,9 @@ paths: responses: '200': description: Customer number validation successful + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -3998,6 +4144,9 @@ paths: responses: '200': description: Successfully retrieved task attachments + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '404': @@ -4067,6 +4216,9 @@ paths: responses: '200': description: Attachment uploaded successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '404': @@ -4170,6 +4322,9 @@ paths: responses: '201': description: Product created successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' put: @@ -4187,6 +4342,9 @@ paths: responses: '200': description: Product updated successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -4225,6 +4383,9 @@ paths: responses: '201': description: Category created successfully + content: + application/json: + schema: {} put: tags: - Categories @@ -4240,6 +4401,9 @@ paths: responses: '200': description: Category updated successfully + content: + application/json: + schema: {} # Bookings Endpoints /bookings: @@ -4276,6 +4440,9 @@ paths: responses: '200': description: Booking updated successfully + content: + application/json: + schema: {} /user/bookings: get: @@ -4309,6 +4476,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} # Goals Endpoints /goals/department: @@ -4512,6 +4682,9 @@ paths: responses: '200': description: Department goal deleted successfully + content: + application/json: + schema: {} '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } @@ -4620,6 +4793,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /admin/bookings/sync: post: @@ -4627,9 +4803,17 @@ paths: - Bookings summary: Sync booking from external system operationId: syncBooking + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Success + content: + application/json: + schema: {} /admin/bookings/department/count: get: @@ -4645,6 +4829,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /user/bookings/washcertificate/download: post: @@ -4664,6 +4851,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /bookings/download_pdf: get: @@ -4679,6 +4869,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /admin/bookings/delete: post: @@ -4698,6 +4891,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/bookings/sync/all: post: @@ -4705,9 +4901,17 @@ paths: - Bookings summary: Sync all bookings from external system operationId: syncAllBookings + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Success + content: + application/json: + schema: {} /admin/bookings/completeWashWithoutWashCertificate: post: @@ -4727,6 +4931,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /user/bookings/delete: post: @@ -4746,6 +4953,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} # Invoices Endpoints /invoices/draft: @@ -4761,6 +4971,9 @@ paths: responses: '200': description: Draft invoices retrieved successfully + content: + application/json: + schema: {} /invoices/draft/close: post: @@ -4781,6 +4994,9 @@ paths: responses: '200': description: Draft invoice closed successfully + content: + application/json: + schema: {} /invoices/pdf: get: @@ -4814,6 +5030,9 @@ paths: responses: '200': description: User invoices retrieved successfully + content: + application/json: + schema: {} /collected-invoices: get: @@ -4828,24 +5047,43 @@ paths: responses: '200': description: Collected invoices retrieved successfully + content: + application/json: + schema: {} post: tags: - Invoices summary: Create collected invoice description: Create a new collected invoice operationId: createCollectedInvoice + requestBody: + required: false + content: + application/json: + schema: {} responses: '201': description: Collected invoice created successfully + content: + application/json: + schema: {} put: tags: - Invoices summary: Update collected invoice description: Update a collected invoice operationId: updateCollectedInvoice + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Collected invoice updated successfully + content: + application/json: + schema: {} /collected-invoices/ready-to-invoice: get: @@ -4857,6 +5095,9 @@ paths: responses: '200': description: Ready invoices retrieved successfully + content: + application/json: + schema: {} /collected-invoices/economic/compare: get: @@ -4893,6 +5134,215 @@ paths: '500': $ref: '#/components/responses/InternalServerError' + /collected-invoices/economic/v2/details: + get: + tags: + - Invoices + summary: Get deep V2 e-conomic invoice details + description: | + Returns normalized internal lines and best-effort fetched draft/booked e-conomic lines + for a collected invoice, including department distributions and warnings. + operationId: getCollectedInvoiceEconomicV2Details + parameters: + - name: collected_invoice_id + in: query + required: true + schema: + type: integer + minimum: 1 + responses: + '200': + description: Details resolved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2DetailsResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /collected-invoices/economic/v2/compare: + get: + tags: + - Invoices + summary: Compare internal invoice with draft/booked (V2) + operationId: compareCollectedInvoiceEconomicV2 + parameters: + - name: collected_invoice_id + in: query + required: true + schema: + type: integer + minimum: 1 + responses: + '200': + description: Comparison completed + content: + application/json: + schema: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2CompareResponse' + examples: + exactMatch: + summary: Exact match between internal and draft/booked + value: + collected_invoice_id: 123 + warnings: [] + comparison: + totals: + internal_net_total: 694 + targets: + draft: + target: draft + status: exact_match + overall_match: true + booked: + target: booked + status: exact_match + overall_match: true + partialMismatch: + summary: Partial mismatch with line and department differences + value: + collected_invoice_id: 123 + warnings: + - Non-billable line count differs + comparison: + totals: + internal_net_total: 694 + targets: + draft: + target: draft + status: partial_mismatch + overall_match: false + mismatch_reasons: + - quantity_mismatch + - department_total_mismatch + missingBooked: + summary: Missing booked target + value: + collected_invoice_id: 123 + comparison: + totals: + internal_net_total: 694 + targets: + booked: + target: booked + status: missing_target + overall_match: false + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '404': { $ref: '#/components/responses/NotFound' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /collected-invoices/economic/v2/compare/bulk: + post: + tags: + - Invoices + summary: Bulk compare collected invoices against draft/booked (V2) + operationId: compareCollectedInvoiceEconomicV2Bulk + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [collected_invoice_ids] + properties: + collected_invoice_ids: + type: array + minItems: 1 + maxItems: 200 + items: + type: integer + minimum: 1 + responses: + '200': + description: Bulk comparison completed + content: + application/json: + schema: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2CompareBulkResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /collected-invoices/economic/v2/revenue-statistics: + get: + tags: + - Invoices + summary: Get overall booked revenue statistics from e-conomic (V2) + description: | + Aggregates booked e-conomic revenue across invoices and lines, with optional filters + for date range, customer(s), department(s), currency, and barred-customer status. + operationId: getCollectedInvoiceEconomicV2RevenueStatistics + parameters: + - name: dateFrom + in: query + required: false + description: Start date (inclusive), defaults to first day of current month. + schema: + type: string + format: date + - name: dateTo + in: query + required: false + description: End date (inclusive), defaults to today. + schema: + type: string + format: date + - name: customer_numbers + in: query + required: false + description: Comma-separated customer numbers to include. + schema: + type: string + example: "42493959,42493960" + - name: department_numbers + in: query + required: false + description: Comma-separated department numbers to include. + schema: + type: string + example: "75,10" + - name: currency + in: query + required: false + description: Restrict to a specific invoice currency. + schema: + type: string + example: "DKK" + - name: barred + in: query + required: false + description: Filter by e-conomic customer barred status. + schema: + type: string + enum: [all, barred, active] + default: all + - name: max_pages + in: query + required: false + description: Safety cap for paginated e-conomic reads. + schema: + type: integer + minimum: 1 + maximum: 200 + default: 10 + responses: + '200': + description: Revenue statistics resolved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2RevenueStatisticsResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + /superuser/invoicing/period: get: tags: @@ -4912,6 +5362,9 @@ paths: responses: '200': description: Invoicing periods retrieved successfully + content: + application/json: + schema: {} /superuser/invoicing/period/distribution/fixed-pricing: get: @@ -4956,6 +5409,151 @@ paths: responses: '200': description: Wash subscriptions distribution retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/InvoicingWashSubscriptionsDistributionResponse' + + /superuser/invoicing/period/distribution/v2/all: + get: + tags: + - Invoices + summary: Get version-aware historical distribution (all) + operationId: getInvoicingPeriodDistributionV2All + parameters: + - name: dateFrom + in: query + required: true + schema: { type: string, format: date } + - name: dateTo + in: query + required: true + schema: { type: string, format: date } + responses: + '200': + description: Version-aware historical distribution (all categories) + content: + application/json: + schema: + $ref: '#/components/schemas/InvoicingDistributionV2AllResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /superuser/invoicing/period/distribution/v2/fixed-pricing: + get: + tags: + - Invoices + summary: Get version-aware historical fixed pricing distribution + operationId: getInvoicingPeriodDistributionV2FixedPricing + parameters: + - name: dateFrom + in: query + required: true + schema: { type: string, format: date } + - name: dateTo + in: query + required: true + schema: { type: string, format: date } + responses: + '200': + description: Version-aware fixed pricing distribution + content: + application/json: + schema: + $ref: '#/components/schemas/InvoicingDistributionV2FixedPricingResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /superuser/invoicing/period/distribution/v2/wash-subscriptions: + get: + tags: + - Invoices + summary: Get version-aware historical wash subscription distribution + operationId: getInvoicingPeriodDistributionV2WashSubscriptions + parameters: + - name: dateFrom + in: query + required: true + schema: { type: string, format: date } + - name: dateTo + in: query + required: true + schema: { type: string, format: date } + responses: + '200': + description: Version-aware wash subscription distribution + content: + application/json: + schema: + $ref: '#/components/schemas/InvoicingDistributionV2WashSubscriptionsResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /superuser/invoicing/period/distribution/v2/customer-prices: + get: + tags: + - Invoices + summary: Get version-aware historical customer-price discount distribution + operationId: getInvoicingPeriodDistributionV2CustomerPrices + parameters: + - name: dateFrom + in: query + required: true + schema: { type: string, format: date } + - name: dateTo + in: query + required: true + schema: { type: string, format: date } + responses: + '200': + description: Version-aware customer-price discount distribution + content: + application/json: + schema: + $ref: '#/components/schemas/InvoicingDistributionV2CustomerPricesResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } + + /superuser/customers/pricing-history: + get: + tags: + - Invoices + summary: Get customer versioned pricing/subscription/discount timeline + operationId: getCustomerPricingHistoryV2 + parameters: + - name: customer_number + in: query + required: true + schema: + type: integer + minimum: 1 + - name: dateFrom + in: query + required: true + schema: { type: string, format: date } + - name: dateTo + in: query + required: true + schema: { type: string, format: date } + responses: + '200': + description: Customer timeline resolved + content: + application/json: + schema: + $ref: '#/components/schemas/CustomerPricingHistoryResponse' + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '403': { $ref: '#/components/responses/Forbidden' } + '500': { $ref: '#/components/responses/InternalServerError' } # Vehicles Endpoints /vehicles: @@ -5041,7 +5639,11 @@ paths: type: integer description: Optional explicit target customer. Defaults to the effective customer context. responses: - '200': {description: Vehicle created} + '200': + description: Vehicle created + content: + application/json: + schema: {} '400': { $ref: '#/components/responses/BadRequest' } '403': { $ref: '#/components/responses/Forbidden' } put: @@ -5077,7 +5679,11 @@ paths: maxLength: 255 nullable: true responses: - '200': {description: Vehicle updated} + '200': + description: Vehicle updated + content: + application/json: + schema: {} '400': { $ref: '#/components/responses/BadRequest' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5099,7 +5705,11 @@ paths: required: true schema: {type: integer} responses: - '200': {description: Vehicle deleted} + '200': + description: Vehicle deleted + content: + application/json: + schema: {} '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5125,6 +5735,9 @@ paths: responses: '200': description: Available addons retrieved successfully + content: + application/json: + schema: {} '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5157,6 +5770,9 @@ paths: responses: '200': description: Vehicle addon toggled successfully + content: + application/json: + schema: {} '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5167,7 +5783,11 @@ paths: summary: Get unknown customer vehicles in department operationId: getUnknownCustomerVehicles responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /department/vehicle/customer-suggestions: get: @@ -5181,7 +5801,11 @@ paths: required: true schema: {type: string} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /vehicles/set-auto-start-on-lpr: post: @@ -5208,7 +5832,11 @@ paths: id: {type: integer} active: {type: boolean} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5240,7 +5868,11 @@ paths: minLength: 1 maxLength: 50 responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} '400': { $ref: '#/components/responses/BadRequest' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } @@ -5252,7 +5884,11 @@ paths: summary: Get users with vehicle subscriptions operationId: getUsersWithVehicleSubscriptions responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /vehicles/status: get: @@ -5266,7 +5902,11 @@ paths: required: true schema: {type: string} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /vehicles/search: get: @@ -5280,7 +5920,11 @@ paths: required: true schema: {type: string} responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} # Notifications Endpoints /notifications: @@ -5317,6 +5961,9 @@ paths: responses: '201': description: Notification created successfully + content: + application/json: + schema: {} delete: tags: - Notifications @@ -5332,6 +5979,9 @@ paths: responses: '200': description: Notification deleted successfully + content: + application/json: + schema: {} # Statistics Endpoints /statistics/bookings/new: @@ -5344,6 +5994,9 @@ paths: responses: '200': description: New bookings statistics retrieved successfully + content: + application/json: + schema: {} /orders/module/stripe/payment_intent: get: @@ -5359,6 +6012,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Orders @@ -5378,6 +6034,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} delete: tags: - Orders @@ -5391,6 +6050,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /orders/module/stripe/payment_intent/capture: post: @@ -5410,6 +6072,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /orders/module/stripe/debug/simulate_payment: post: @@ -5429,6 +6094,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /statistics/orders/new: get: @@ -5440,6 +6108,9 @@ paths: responses: '200': description: New orders statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/today: get: @@ -5451,6 +6122,9 @@ paths: responses: '200': description: Today's income statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/yesterday: get: @@ -5462,6 +6136,9 @@ paths: responses: '200': description: Yesterday's income statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/this-month: get: @@ -5473,6 +6150,9 @@ paths: responses: '200': description: This month's income statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/last-month: get: @@ -5484,6 +6164,9 @@ paths: responses: '200': description: Last month's income statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/this-year: get: @@ -5495,6 +6178,9 @@ paths: responses: '200': description: This year's income statistics retrieved successfully + content: + application/json: + schema: {} /statistics/income/departments: get: @@ -5503,7 +6189,11 @@ paths: summary: Get total income today by departments operationId: getTotalIncomeTodayByDepartments responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /statistics/economic/totals: get: @@ -5512,7 +6202,11 @@ paths: summary: Get total economic statistics operationId: getEconomicTotals responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /statistics/economic/totals/department_sent_invoice_totals: get: @@ -5521,7 +6215,11 @@ paths: summary: Get department sent invoice totals operationId: getDepartmentSentInvoiceTotals responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} /statistics/economic/totals/department_draft_invoice_totals: get: @@ -5530,7 +6228,11 @@ paths: summary: Get department draft invoice totals operationId: getDepartmentDraftInvoiceTotals responses: - '200': {description: Success} + '200': + description: Success + content: + application/json: + schema: {} # Worker Endpoints /worker/version: @@ -5543,6 +6245,9 @@ paths: responses: '200': description: Worker version retrieved successfully + content: + application/json: + schema: {} /worker/update-version: get: @@ -5560,6 +6265,9 @@ paths: responses: '200': description: Version update target set successfully + content: + application/json: + schema: {} /worker/status: get: @@ -5571,6 +6279,9 @@ paths: responses: '200': description: Worker status retrieved successfully + content: + application/json: + schema: {} /worker/debug: get: @@ -5582,6 +6293,9 @@ paths: responses: '200': description: Debug information retrieved successfully + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' @@ -5594,6 +6308,9 @@ paths: responses: '200': description: Worker debug enabled + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' @@ -5606,6 +6323,9 @@ paths: responses: '200': description: Worker debug disabled + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' @@ -5619,6 +6339,9 @@ paths: responses: '200': description: License plates retrieved successfully + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' @@ -5637,6 +6360,9 @@ paths: responses: '200': description: Customer check completed + content: + application/json: + schema: {} '404': $ref: '#/components/responses/NotFound' @@ -5656,6 +6382,9 @@ paths: responses: '200': description: CVR information retrieved successfully + content: + application/json: + schema: {} /cvr/search: get: @@ -5674,6 +6403,9 @@ paths: responses: '200': description: Search results retrieved successfully + content: + application/json: + schema: {} # Plate Scans Endpoints /numberplatescans: @@ -5690,6 +6422,9 @@ paths: responses: '200': description: Plate scans retrieved successfully + content: + application/json: + schema: {} post: tags: - Plate Scans @@ -5713,6 +6448,9 @@ paths: responses: '201': description: Plate scan recorded successfully + content: + application/json: + schema: {} /numberplatescans/department: post: @@ -5737,6 +6475,9 @@ paths: responses: '201': description: Plate scan recorded successfully + content: + application/json: + schema: {} /numberplatescans/post: get: @@ -5747,6 +6488,9 @@ paths: responses: '200': description: Post-scan results retrieved successfully + content: + application/json: + schema: {} /numberplatescanners: get: @@ -5762,6 +6506,9 @@ paths: responses: '200': description: Plate scanners retrieved successfully + content: + application/json: + schema: {} post: tags: - Plate Scans @@ -5781,6 +6528,9 @@ paths: responses: '201': description: Plate scanner added successfully + content: + application/json: + schema: {} put: tags: - Plate Scans @@ -5801,6 +6551,9 @@ paths: responses: '200': description: Plate scanner updated successfully + content: + application/json: + schema: {} /department/numberplatescanners: get: @@ -5817,6 +6570,9 @@ paths: responses: '200': description: Department plate scanners retrieved successfully + content: + application/json: + schema: {} /relay/button/press/post: get: @@ -5832,6 +6588,9 @@ paths: responses: '201': description: Button press recorded + content: + application/json: + schema: {} # Module - e-conomic Endpoints /economic/customers/import: @@ -5841,9 +6600,17 @@ paths: summary: Import e-conomic customers description: Import customers from e-conomic operationId: importEconomicCustomers + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Customers imported successfully + content: + application/json: + schema: {} /economic/departments: get: @@ -5854,6 +6621,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /economic/products: get: @@ -5864,6 +6634,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/economic/customer: get: @@ -5879,6 +6652,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Modules @@ -5900,6 +6676,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /economic/layouts: get: @@ -5911,6 +6690,9 @@ paths: responses: '200': description: Layouts retrieved successfully + content: + application/json: + schema: {} /economic/payment-terms: get: @@ -5922,6 +6704,9 @@ paths: responses: '200': description: Payment terms retrieved successfully + content: + application/json: + schema: {} /economic/invoice/draft/export: post: @@ -5930,9 +6715,17 @@ paths: summary: Export draft invoice to e-conomic description: Export a draft invoice to e-conomic operationId: exportDraftInvoiceToEconomic + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Draft invoice exported successfully + content: + application/json: + schema: {} /economic/invoice/export: post: @@ -5941,9 +6734,17 @@ paths: summary: Export invoice to e-conomic description: Export a booked invoice to e-conomic operationId: exportInvoiceToEconomic + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Invoice exported successfully + content: + application/json: + schema: {} # Module - Stripe Endpoints /modules/stripe/customers: @@ -5956,6 +6757,9 @@ paths: responses: '200': description: Stripe customers retrieved successfully + content: + application/json: + schema: {} /modules/stripe/products: get: @@ -5967,6 +6771,9 @@ paths: responses: '200': description: Stripe products retrieved successfully + content: + application/json: + schema: {} /modules/stripe/prices: get: @@ -5978,6 +6785,9 @@ paths: responses: '200': description: Stripe prices retrieved successfully + content: + application/json: + schema: {} /modules/stripe/invoice: post: @@ -5986,9 +6796,17 @@ paths: summary: Create Stripe invoice description: Create an invoice in Stripe operationId: createStripeInvoice + requestBody: + required: false + content: + application/json: + schema: {} responses: '201': description: Stripe invoice created successfully + content: + application/json: + schema: {} /modules/stripe/terminal/readers: get: @@ -5999,6 +6817,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/stripe/terminal/locations: get: @@ -6009,6 +6830,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/stripe/department/terminal/location: get: @@ -6024,6 +6848,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Modules @@ -6042,6 +6869,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/stripe/department/terminal/readers: get: @@ -6057,6 +6887,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} # Module - Backup Endpoints /modules/backup/backups: @@ -6068,6 +6901,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Modules @@ -6086,6 +6922,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} # Module - XLVask Endpoints /modules/xlvask/usageLog: @@ -6101,6 +6940,9 @@ paths: responses: '200': description: Usage logs retrieved successfully + content: + application/json: + schema: {} /modules/xlvask/vehicles: get: @@ -6112,6 +6954,9 @@ paths: responses: '200': description: XLVask vehicles retrieved successfully + content: + application/json: + schema: {} /modules/xlvask/customers: get: @@ -6123,6 +6968,9 @@ paths: responses: '200': description: XLVask customers retrieved successfully + content: + application/json: + schema: {} /modules/action-logs: get: @@ -6423,6 +7271,9 @@ paths: responses: '200': description: Vehicle information retrieved successfully + content: + application/json: + schema: {} /modules/virkdata/search: get: @@ -6440,6 +7291,9 @@ paths: responses: '200': description: Company information retrieved successfully + content: + application/json: + schema: {} /modules/fxratesapi/rate: get: @@ -6462,6 +7316,9 @@ paths: responses: '200': description: Exchange rate retrieved successfully + content: + application/json: + schema: {} /modules/fxratesapi/rates: get: @@ -6473,6 +7330,9 @@ paths: responses: '200': description: Exchange rates retrieved successfully + content: + application/json: + schema: {} /modules/weatherapi/current: @@ -6590,6 +7450,9 @@ paths: responses: '200': description: Entra users retrieved successfully + content: + application/json: + schema: {} # Attachments Endpoints /attachments/upload: @@ -6612,6 +7475,9 @@ paths: responses: '201': description: Attachment uploaded successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' @@ -6631,6 +7497,9 @@ paths: responses: '200': description: Order attachments retrieved successfully + content: + application/json: + schema: {} /orders/attachments/upload: post: @@ -6654,6 +7523,9 @@ paths: responses: '201': description: Order attachment uploaded successfully + content: + application/json: + schema: {} /orders/attachments/download: get: @@ -6693,6 +7565,9 @@ paths: responses: '200': description: Form retrieved successfully + content: + application/json: + schema: {} post: tags: - Forms @@ -6719,6 +7594,9 @@ paths: responses: '201': description: Form submitted successfully + content: + application/json: + schema: {} # Permissions Endpoints /permissions: @@ -6754,24 +7632,43 @@ paths: responses: '200': description: Customer attributes retrieved successfully + content: + application/json: + schema: {} post: tags: - Users summary: Add customer attribute description: Add a custom attribute to a customer operationId: addCustomerAttribute + requestBody: + required: false + content: + application/json: + schema: {} responses: '201': description: Customer attribute added successfully + content: + application/json: + schema: {} delete: tags: - Users summary: Delete customer attribute description: Remove a custom attribute from a customer operationId: deleteCustomerAttribute + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Customer attribute deleted successfully + content: + application/json: + schema: {} /customer/notes: get: @@ -6788,24 +7685,43 @@ paths: responses: '200': description: Customer notes retrieved successfully + content: + application/json: + schema: {} post: tags: - Users summary: Add customer note description: Add a note to a customer operationId: addCustomerNote + requestBody: + required: false + content: + application/json: + schema: {} responses: '201': description: Customer note added successfully + content: + application/json: + schema: {} delete: tags: - Users summary: Delete customer note description: Remove a note from a customer operationId: deleteCustomerNote + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Customer note deleted successfully + content: + application/json: + schema: {} /customers/search: post: @@ -6826,6 +7742,155 @@ paths: responses: '200': description: Customers found successfully + content: + application/json: + schema: {} + + /search/system: + get: + tags: + - Search + summary: System-wide search + description: Search across all supported entities with permission-aware filtering and optional intent parsing debug metadata. + operationId: systemWideSearchGet + parameters: + - in: query + name: query + required: true + schema: + type: string + description: Free-text query to search for. + - in: query + name: include_types + required: false + schema: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + style: form + explode: false + description: Comma-separated list of entity types to include. Defaults to all allowed types. + - in: query + name: exclude_types + required: false + schema: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + style: form + explode: false + description: Comma-separated list of entity types to exclude. + - in: query + name: include_associations + required: false + schema: + type: boolean + default: true + description: Include associated objects when matching a primary entity such as a customer. + - in: query + name: debug_intent + required: false + schema: + type: boolean + default: false + description: Include intent parser diagnostics in `meta.intent_parser`. + - in: query + name: limit + required: false + schema: + type: integer + minimum: 1 + maximum: 200 + default: 50 + - in: query + name: offset + required: false + schema: + type: integer + minimum: 0 + default: 0 + responses: + '200': + description: Search results returned successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + post: + tags: + - Search + summary: System-wide search + description: Search across all supported entities using JSON request payload. + operationId: systemWideSearchPost + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchRequest' + responses: + '200': + description: Search results returned successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + + /superuser/search/system/cache: + delete: + tags: + - Search + summary: Clear system search caches + description: Clears both query-result cache and intent-parser cache namespaces for system-wide search. + operationId: clearSystemSearchCache + responses: + '200': + description: Cache cleared successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchCacheClearResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + + /superuser/search/system/cache/rebuild: + post: + tags: + - Search + summary: Queue system search cache rebuild + description: Queues a cache rebuild request and clears active query/intent cache namespaces immediately. + operationId: rebuildSystemSearchCache + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchCacheRebuildRequest' + responses: + '200': + description: Cache rebuild queued successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SystemSearchCacheRebuildResponse' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' # Configuration Endpoints /economic/config: @@ -6844,6 +7909,11 @@ paths: tags: [Config] summary: Update e-conomic config operationId: updateEconomicConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: e-conomic configuration updated successfully @@ -6868,6 +7938,11 @@ paths: tags: [Config] summary: Update reCAPTCHA config operationId: updateRecaptchaConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: reCAPTCHA configuration updated successfully @@ -6892,6 +7967,11 @@ paths: tags: [Config] summary: Update email config operationId: updateEmailConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Email configuration updated successfully @@ -6905,6 +7985,11 @@ paths: tags: [Config] summary: Test email config operationId: testEmailConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Email configuration test completed @@ -6929,6 +8014,11 @@ paths: tags: [Config] summary: Update backups config operationId: updateBackupsConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Backups configuration updated successfully @@ -6953,6 +8043,11 @@ paths: tags: [Config] summary: Update Bird config operationId: updateBirdConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Bird configuration updated successfully @@ -6977,6 +8072,11 @@ paths: tags: [Config] summary: Update MotorAPI config operationId: updateMotorApiConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: MotorAPI configuration updated successfully @@ -7001,6 +8101,11 @@ paths: tags: [Config] summary: Update Stripe config operationId: updateStripeConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Stripe configuration updated successfully @@ -7025,6 +8130,11 @@ paths: tags: [Config] summary: Update FXRatesAPI config operationId: updateFxRatesApiConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: FXRatesAPI configuration updated successfully @@ -7050,6 +8160,11 @@ paths: tags: [Config] summary: Update WeatherAPI config operationId: updateWeatherApiConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: WeatherAPI configuration updated successfully @@ -7074,6 +8189,11 @@ paths: tags: [Config] summary: Update GatewayAPI config operationId: updateGatewayApiConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: GatewayAPI configuration updated successfully @@ -7098,6 +8218,11 @@ paths: tags: [Config] summary: Update XLVask config operationId: updateXlvaskConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: XLVask configuration updated successfully @@ -7122,6 +8247,11 @@ paths: tags: [Config] summary: Update Entra config operationId: updateEntraConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Entra configuration updated successfully @@ -7146,6 +8276,11 @@ paths: tags: [Config] summary: Update Limble config operationId: updateLimbleConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Limble configuration updated successfully @@ -7170,6 +8305,11 @@ paths: tags: [Config] summary: Update OcrSpace config operationId: updateOcrSpaceConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: OcrSpace configuration updated successfully @@ -7194,6 +8334,11 @@ paths: tags: [Config] summary: Update OpenAI config operationId: updateOpenAiConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: OpenAI configuration updated successfully @@ -7218,6 +8363,11 @@ paths: tags: [Config] summary: Update LicensePlateRecognizer config operationId: updateLicensePlateRecognizerConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: LicensePlateRecognizer configuration updated successfully @@ -7242,6 +8392,11 @@ paths: tags: [Config] summary: Update Virkdata config operationId: updateVirkdataConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Virkdata configuration updated successfully @@ -7266,6 +8421,11 @@ paths: tags: [Config] summary: Update Shelly config operationId: updateShellyConfig + requestBody: + required: false + content: + application/json: + schema: {} responses: '200': description: Shelly configuration updated successfully @@ -7322,6 +8482,9 @@ paths: responses: '200': description: Branding options retrieved successfully + content: + application/json: + schema: {} '400': $ref: '#/components/responses/BadRequest' '403': @@ -7346,6 +8509,9 @@ paths: responses: '200': description: Branding option added successfully + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' put: @@ -7369,6 +8535,9 @@ paths: responses: '200': description: Branding option updated successfully + content: + application/json: + schema: {} '403': $ref: '#/components/responses/Forbidden' @@ -7381,6 +8550,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Roles @@ -7398,6 +8570,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} put: tags: - Roles @@ -7416,6 +8591,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /roles/permissions: post: @@ -7436,6 +8614,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} delete: tags: - Roles @@ -7453,6 +8634,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /roles/clone: post: @@ -7473,6 +8657,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/washcertificates: get: @@ -7483,6 +8670,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/xlvask/services/usage/orders: get: @@ -7493,6 +8683,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /modules/xlvask/services/usage/orders/fast-link: get: @@ -7503,6 +8696,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/department: get: @@ -7513,6 +8709,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/department/prices: get: @@ -7528,6 +8727,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Departments @@ -7547,6 +8749,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /superuser/department/variables: get: @@ -7562,6 +8767,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Departments @@ -7581,6 +8789,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /departments/daily-reports: get: @@ -7591,6 +8802,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} post: tags: - Departments @@ -7610,6 +8824,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} put: tags: - Departments @@ -7628,6 +8845,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /departments/daily-reports/get: get: @@ -7647,6 +8867,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /departments/daily-reports/product-count: get: @@ -7674,6 +8897,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /departments/daily-reports/transaction-count: get: @@ -7697,6 +8923,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} /departments/daily-reports/bookings-count: get: @@ -7716,6 +8945,9 @@ paths: responses: '200': description: Success + content: + application/json: + schema: {} # Account Security - Passkeys /account/security/passkeys: @@ -7735,6 +8967,10 @@ paths: $ref: '#/components/schemas/Passkey' '400': description: Invalid session or request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' post: tags: - Security @@ -7758,6 +8994,10 @@ paths: type: integer '400': description: Invalid session or request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /account/security/passkeys/{id}: patch: @@ -7780,8 +9020,15 @@ paths: responses: '200': description: Passkey renamed + content: + application/json: + schema: {} '404': description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' delete: tags: - Security @@ -7796,8 +9043,15 @@ paths: responses: '200': description: Passkey deleted + content: + application/json: + schema: {} '404': description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' components: securitySchemes: @@ -7900,6 +9154,328 @@ components: type: integer description: HTTP status code + SystemSearchEntityType: + type: string + enum: + - objects + - module_config + - orders + - order_items + - customers + - employees + - users + - subusers + - customer_discounts + - customer_fixed_prices + - departments + - permissions + - roles + - invoices + - vehicles + - bookings + - bookings_new + - branding + - categories + - currency_conversion_rates + - customer_codes + - customer_default_department + - customer_notes + - customer_vehicles_addons + - department_categories + - department_daily_reports + - department_gates + - department_goals + - department_lanes + - department_notification_sms + - department_relays + - department_selfserve_condition_rules + - department_selfserve_conditions + - department_selfserve_questions + - department_selfserve_tasks + - department_selfserve_vehicle_conditions + - department_time_bookings_entries + - department_time_bookings_opening_hours + - department_time_bookings_types + - department_variables + - fxratesapi_conversion_rates + - module_action_logs + - motorapi_lookups + - notifications + - order_bookings + - plate_scanners + - plate_scans + - product_options + - products + - stripe_module_customers + - stripe_module_orders + - stripe_payment_intents + - subuser_grants + - xlvask_customers + - xlvask_potential_order_matches + - xlvask_usage_log_wash_items + - xlvask_usage_logs + - xlvask_vehicle_types + - xlvask_vehicles + + SystemSearchRequest: + type: object + required: + - query + properties: + query: + type: string + description: Free-text query to search for. + include_types: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + description: Limit search to these entity types. + exclude_types: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + description: Exclude these entity types from search. + include_associations: + type: boolean + default: true + description: Include associated records for matched core entities. + debug_intent: + type: boolean + default: false + description: Include parser diagnostics in `meta.intent_parser`. + limit: + type: integer + minimum: 1 + maximum: 200 + default: 50 + offset: + type: integer + minimum: 0 + default: 0 + + SystemSearchResult: + type: object + properties: + entity_type: + $ref: '#/components/schemas/SystemSearchEntityType' + entity_id: + type: string + title: + type: string + description: + type: string + customer_number: + type: integer + nullable: true + department_id: + type: integer + nullable: true + score: + type: integer + association_reason: + type: string + nullable: true + payload: + type: object + additionalProperties: true + required: + - entity_type + - entity_id + - title + - score + + SystemSearchIntentParserMeta: + type: object + properties: + invoked: + type: boolean + source: + type: string + enum: [cache, openai, none] + status: + type: string + confidence: + type: number + minimum: 0 + maximum: 1 + expanded_terms: + type: array + items: + type: string + entity_hints: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + fallback_reason: + type: string + nullable: true + required: + - invoked + - source + - status + - confidence + - expanded_terms + - entity_hints + + SystemSearchMeta: + type: object + properties: + query: + type: string + limit: + type: integer + offset: + type: integer + total: + type: integer + allowed_types: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + cache: + type: object + properties: + hit: + type: boolean + required: [hit] + intent_parser: + $ref: '#/components/schemas/SystemSearchIntentParserMeta' + required: + - query + - limit + - offset + - total + - allowed_types + - cache + + SystemSearchPayload: + type: object + properties: + results: + type: array + items: + $ref: '#/components/schemas/SystemSearchResult' + grouped_results: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/SystemSearchResult' + meta: + $ref: '#/components/schemas/SystemSearchMeta' + required: + - results + - grouped_results + - meta + + SystemSearchResponse: + type: object + properties: + success: + type: boolean + data: + $ref: '#/components/schemas/SystemSearchPayload' + meta: + type: object + additionalProperties: true + includes: + type: object + additionalProperties: true + required: + - success + - data + - meta + - includes + + SystemSearchCacheClearResponse: + type: object + properties: + success: + type: boolean + data: + type: object + properties: + message: + type: string + query_cache_cleared: + type: boolean + intent_cache_cleared: + type: boolean + required: + - message + - query_cache_cleared + - intent_cache_cleared + meta: + type: object + additionalProperties: true + includes: + type: object + additionalProperties: true + required: + - success + - data + - meta + - includes + + SystemSearchCacheRebuildRequest: + type: object + properties: + scope: + type: string + enum: [all, types, dirty] + default: all + types: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + + SystemSearchCacheRebuildResponse: + type: object + properties: + success: + type: boolean + data: + type: object + properties: + message: + type: string + request: + type: object + properties: + scope: + type: string + enum: [all, types, dirty] + types: + type: array + items: + $ref: '#/components/schemas/SystemSearchEntityType' + requested_at: + type: integer + required: + - scope + - types + - requested_at + query_cache_cleared: + type: boolean + intent_cache_cleared: + type: boolean + required: + - message + - request + - query_cache_cleared + - intent_cache_cleared + meta: + type: object + additionalProperties: true + includes: + type: object + additionalProperties: true + required: + - success + - data + - meta + - includes + ModuleConfigValue: oneOf: - type: string @@ -8791,23 +10367,742 @@ components: type: number format: float nullable: true - description: draft_total minus booked_total when both are available + description: Selected e-conomic total (draft when available, otherwise booked) minus internal_total example: 0 internal_total: type: number format: float description: Internal total amount for the collected invoice example: 694 - order_ids: - type: array - description: List of order IDs included in the collected invoice - items: - type: integer - example: [38679, 39210] required: - collected_invoice_id - internal_total + + CollectedInvoiceEconomicV2DetailsResponse: + type: object + properties: + collected_invoice_id: + type: integer + external_id: + type: string + order_ids: + type: array + items: + type: integer + economic: + type: object + properties: + draft_id: + type: integer + nullable: true + booked_id: + type: integer + nullable: true + customer: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2CustomerSummary' + internal: + type: object + required: [normalized] + properties: + normalized: + $ref: '#/components/schemas/EconomicV2NormalizedInvoice' + draft: + type: object + properties: + exists: + type: boolean + raw: + type: object + nullable: true + additionalProperties: true + normalized: + allOf: + - $ref: '#/components/schemas/EconomicV2NormalizedInvoice' + nullable: true + booked: + type: object + properties: + exists: + type: boolean + raw: + type: object + nullable: true + additionalProperties: true + normalized: + allOf: + - $ref: '#/components/schemas/EconomicV2NormalizedInvoice' + nullable: true + warnings: + type: array + items: + type: string + required: + - collected_invoice_id - order_ids + - economic + - customer + - internal + - draft + - booked + - warnings + + CollectedInvoiceEconomicV2CustomerSummary: + type: object + properties: + internal_customer_number: + type: integer + nullable: true + draft_customer_number: + type: integer + nullable: true + booked_customer_number: + type: integer + nullable: true + exists: + type: boolean + name: + type: string + nullable: true + barred: + type: boolean + nullable: true + required: + - exists + + CollectedInvoiceEconomicV2CompareResponse: + type: object + properties: + collected_invoice_id: + type: integer + details: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2DetailsResponse' + comparison: + $ref: '#/components/schemas/EconomicV2Comparison' + warnings: + type: array + items: + type: string + required: + - collected_invoice_id + - details + - comparison + - warnings + + CollectedInvoiceEconomicV2CompareBulkResponse: + type: object + properties: + requested: + type: integer + compared: + type: integer + failed: + type: integer + results: + type: array + items: + $ref: '#/components/schemas/CollectedInvoiceEconomicV2CompareResponse' + errors: + type: array + items: + type: object + properties: + collected_invoice_id: + type: integer + error: + type: string + required: + - requested + - compared + - failed + - results + - errors + + CollectedInvoiceEconomicV2RevenueStatisticsResponse: + type: object + properties: + filters: + type: object + properties: + dateFrom: + type: string + format: date + dateTo: + type: string + format: date + customer_numbers: + type: array + items: + type: integer + department_numbers: + type: array + items: + type: integer + currency: + type: string + nullable: true + barred: + type: string + enum: [all, barred, active] + max_pages: + type: integer + summary: + $ref: '#/components/schemas/EconomicV2RevenueSummary' + customers: + type: array + items: + $ref: '#/components/schemas/EconomicV2RevenueCustomerStat' + departments: + type: array + items: + $ref: '#/components/schemas/EconomicV2RevenueDepartmentStat' + currencies: + type: array + items: + $ref: '#/components/schemas/EconomicV2RevenueCurrencyStat' + warnings: + type: array + items: + type: string + required: + - filters + - summary + - customers + - departments + - currencies + - warnings + + EconomicV2RevenueSummary: + type: object + properties: + invoice_count: + type: integer + line_count: + type: integer + unique_customers: + type: integer + net_amount: + type: number + vat_amount: + type: number + gross_amount: + type: number + average_invoice_net_amount: + type: number + required: + - invoice_count + - line_count + - unique_customers + - net_amount + - vat_amount + - gross_amount + - average_invoice_net_amount + + EconomicV2RevenueCustomerStat: + type: object + properties: + customer_number: + type: integer + customer_name: + type: string + nullable: true + barred: + type: boolean + nullable: true + invoice_count: + type: integer + net_amount: + type: number + vat_amount: + type: number + gross_amount: + type: number + required: + - customer_number + - invoice_count + - net_amount + - vat_amount + - gross_amount + + EconomicV2RevenueDepartmentStat: + type: object + properties: + department_key: + type: string + department_number: + type: integer + nullable: true + invoice_count: + type: integer + line_count: + type: integer + net_amount: + type: number + vat_amount: + type: number + gross_amount: + type: number + required: + - department_key + - invoice_count + - line_count + - net_amount + - vat_amount + - gross_amount + + EconomicV2RevenueCurrencyStat: + type: object + properties: + currency: + type: string + invoice_count: + type: integer + net_amount: + type: number + vat_amount: + type: number + gross_amount: + type: number + required: + - currency + - invoice_count + - net_amount + - vat_amount + - gross_amount + + EconomicV2NormalizedInvoice: + type: object + properties: + source: + type: string + enum: [internal, draft, booked] + totals: + type: object + properties: + net_total: + type: number + line_net_total: + type: number + line_count: + type: integer + billable_line_count: + type: integer + difference_from_line_sum: + type: number + nullable: true + departments: + $ref: '#/components/schemas/EconomicV2DepartmentDistribution' + lines: + type: array + items: + $ref: '#/components/schemas/EconomicV2NormalizedLineItem' + warnings: + type: array + items: + type: string + required: + - source + - totals + - departments + - lines + - warnings + + EconomicV2NormalizedLineItem: + type: object + properties: + index: + type: integer + source: + type: string + source_order_id: + type: integer + nullable: true + source_line_id: + type: integer + nullable: true + line_type: + type: string + enum: [product, discount, text] + billable: + type: boolean + product_number: + type: string + nullable: true + product_id: + type: integer + nullable: true + description: + type: string + reference: + type: string + quantity: + type: number + unit_net_price: + type: number + line_net_amount: + type: number + department_distribution: + $ref: '#/components/schemas/EconomicV2DepartmentDistribution' + match_key: + type: string + required: + - source + - line_type + - billable + - description + - reference + - quantity + - unit_net_price + - line_net_amount + - department_distribution + - match_key + + EconomicV2DepartmentDistribution: + type: object + additionalProperties: + type: number + example: + "75": 100 + + EconomicV2Comparison: + type: object + properties: + totals: + type: object + properties: + internal_net_total: + type: number + targets: + type: object + properties: + draft: + $ref: '#/components/schemas/EconomicV2TargetComparison' + booked: + $ref: '#/components/schemas/EconomicV2TargetComparison' + warnings: + type: array + items: + type: string + required: + - totals + - targets + - warnings + + EconomicV2TargetComparison: + type: object + properties: + target: + type: string + enum: [draft, booked] + status: + type: string + enum: [exact_match, partial_mismatch, total_mismatch, missing_target] + overall_match: + type: boolean + totals: + $ref: '#/components/schemas/EconomicV2TotalsComparison' + lines: + type: object + properties: + summary: + type: object + properties: + internal_billable_count: + type: integer + target_billable_count: + type: integer + mismatch_count: + type: integer + diff: + type: array + items: + $ref: '#/components/schemas/EconomicV2LineDiffEntry' + departments: + type: object + properties: + matches: + type: boolean + diff: + type: array + items: + $ref: '#/components/schemas/EconomicV2DepartmentDiffEntry' + mismatch_reasons: + type: array + items: + type: string + warnings: + type: array + items: + type: string + required: + - target + - status + - overall_match + - totals + - lines + - departments + - mismatch_reasons + - warnings + + EconomicV2TotalsComparison: + type: object + properties: + internal_net_total: + type: number + nullable: true + target_net_total: + type: number + nullable: true + difference: + type: number + nullable: true + abs_difference: + type: number + nullable: true + matches: + type: boolean + required: + - matches + + EconomicV2LineDiffEntry: + type: object + properties: + match_key: + type: string + reasons: + type: array + items: + type: string + internal_line: + allOf: + - $ref: '#/components/schemas/EconomicV2NormalizedLineItem' + nullable: true + target_line: + allOf: + - $ref: '#/components/schemas/EconomicV2NormalizedLineItem' + nullable: true + required: + - match_key + - reasons + + EconomicV2DepartmentDiffEntry: + type: object + properties: + department_key: + type: string + internal_amount: + type: number + target_amount: + type: number + difference: + type: number + matches: + type: boolean + required: + - department_key + - internal_amount + - target_amount + - difference + - matches + + InvoicingDistributionV2Transaction: + type: object + properties: + id: + type: integer + date: + type: string + format: date-time + amount: + type: number + booked: + type: boolean + department_id: + type: integer + excluded: + type: boolean + required: [id, date, amount, booked, department_id, excluded] + + InvoicingDistributionV2Customer: + type: object + properties: + id: + type: integer + nullable: true + customer_number: + type: integer + customer_name: + type: string + transactions: + type: array + items: + $ref: '#/components/schemas/InvoicingDistributionV2Transaction' + requires_action: + type: boolean + meta: + type: object + additionalProperties: true + required: [customer_number, customer_name, transactions, requires_action, meta] + + InvoicingDistributionV2CategoryResponse: + type: object + properties: + customers: + type: array + items: + $ref: '#/components/schemas/InvoicingDistributionV2Customer' + collective_results: + type: object + additionalProperties: true + warnings: + type: array + items: + type: string + required: [customers, collective_results, warnings] + + InvoicingDistributionV2FixedPricingResponse: + $ref: '#/components/schemas/InvoicingDistributionV2CategoryResponse' + + InvoicingDistributionV2WashSubscriptionsResponse: + $ref: '#/components/schemas/InvoicingDistributionV2CategoryResponse' + + InvoicingDistributionV2CustomerPricesResponse: + $ref: '#/components/schemas/InvoicingDistributionV2CategoryResponse' + + InvoicingDistributionV2AllResponse: + type: object + properties: + fixed_pricing: + $ref: '#/components/schemas/InvoicingDistributionV2FixedPricingResponse' + wash_subscriptions: + $ref: '#/components/schemas/InvoicingDistributionV2WashSubscriptionsResponse' + customer_prices: + $ref: '#/components/schemas/InvoicingDistributionV2CustomerPricesResponse' + required: [fixed_pricing, wash_subscriptions, customer_prices] + + PricingHistoryVersionEntry: + type: object + properties: + id: + type: integer + type: + type: string + enum: [fixed_pricing, vehicle_subscription, discount_override] + customer_number: + type: integer + effective_from: + type: string + format: date-time + effective_to: + type: string + format: date-time + nullable: true + source: + type: string + confidence: + type: number + minimum: 0 + maximum: 1 + inferred: + type: boolean + metadata_json: + oneOf: + - type: string + - type: object + additionalProperties: true + - type: array + items: {} + nullable: true + required: + - id + - type + - customer_number + - effective_from + - source + - confidence + - inferred + + CustomerPricingHistoryResponse: + type: object + properties: + customer_number: + type: integer + fixed_pricing: + type: array + items: + type: object + additionalProperties: true + vehicle_subscriptions: + type: array + items: + type: object + additionalProperties: true + discount_overrides: + type: array + items: + type: object + additionalProperties: true + timeline: + type: array + items: + $ref: '#/components/schemas/PricingHistoryVersionEntry' + required: + - customer_number + - fixed_pricing + - vehicle_subscriptions + - discount_overrides + - timeline + + InvoicingWashSubscriptionsDistributionResponse: + type: object + properties: + success: + type: boolean + example: true + data: + type: array + items: + $ref: '#/components/schemas/InvoicingWashSubscriptionsDistributionCustomer' + meta: + $ref: '#/components/schemas/InvoicingFixedPricingDistributionMeta' + includes: + type: object + additionalProperties: true + required: + - success + - data + - meta + - includes + + InvoicingWashSubscriptionsDistributionCustomer: + type: object + properties: + id: + type: integer + nullable: true + customer_number: + type: integer + customer_name: + type: string + transactions: + type: array + items: + $ref: '#/components/schemas/InvoicingFixedPricingDistributionTransaction' + requires_action: + type: boolean + meta: + type: object + properties: + subscription: + type: object + additionalProperties: true + required: + - subscription + required: + - customer_number + - customer_name + - transactions + - requires_action + - meta InvoicingFixedPricingDistributionResponse: type: object diff --git a/src/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue b/src/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue index e08d996b..419f3962 100644 --- a/src/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue +++ b/src/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue @@ -392,6 +392,103 @@ export const CollectedOrderInvoices = { throw error; }); }, + v2: { + details: async (collectedInvoiceId) => { + return authenticatedRequest('/collected-invoices/economic/v2/details', 'GET', { + collected_invoice_id: parseInt(collectedInvoiceId), + }).then((response) => { + console.warn(response); + return response; + }).catch((error) => { + console.warn(error); + throw error; + }); + }, + compare: async (collectedInvoiceId) => { + return authenticatedRequest('/collected-invoices/economic/v2/compare', 'GET', { + collected_invoice_id: parseInt(collectedInvoiceId), + }).then((response) => { + console.warn(response); + return response; + }).catch((error) => { + console.warn(error); + throw error; + }); + }, + compareBulk: async (collectedInvoiceIds = []) => { + const MAX_BULK_COMPARE_IDS = 200; + const sanitizedIds = (Array.isArray(collectedInvoiceIds) ? collectedInvoiceIds : []) + .map((id) => parseInt(id)) + .filter((id) => Number.isInteger(id) && id > 0); + + if (!sanitizedIds.length) { + return { + data: { + data: { + requested: 0, + compared: 0, + failed: 0, + results: [], + errors: [], + }, + }, + }; + } + + if (sanitizedIds.length <= MAX_BULK_COMPARE_IDS) { + return authenticatedRequest('/collected-invoices/economic/v2/compare/bulk', 'POST', { + collected_invoice_ids: sanitizedIds, + }).then((response) => { + console.warn(response); + return response; + }).catch((error) => { + console.warn(error); + throw error; + }); + } + + const aggregated = { + requested: sanitizedIds.length, + compared: 0, + failed: 0, + results: [], + errors: [], + }; + + for (let index = 0; index < sanitizedIds.length; index += MAX_BULK_COMPARE_IDS) { + const chunkIds = sanitizedIds.slice(index, index + MAX_BULK_COMPARE_IDS); + const response = await authenticatedRequest('/collected-invoices/economic/v2/compare/bulk', 'POST', { + collected_invoice_ids: chunkIds, + }); + const payload = response?.data?.data || response?.data || {}; + const results = Array.isArray(payload?.results) ? payload.results : []; + const errors = Array.isArray(payload?.errors) ? payload.errors : []; + const comparedCount = Number(payload?.compared); + const failedCount = Number(payload?.failed); + + aggregated.compared += Number.isFinite(comparedCount) ? comparedCount : results.length; + aggregated.failed += Number.isFinite(failedCount) ? failedCount : errors.length; + aggregated.results.push(...results); + aggregated.errors.push(...errors); + } + + return { + data: { + data: aggregated, + }, + }; + }, + revenueStatistics: async (params = {}) => { + return authenticatedRequest('/collected-invoices/economic/v2/revenue-statistics', 'GET', params) + .then((response) => { + console.warn(response); + return response; + }).catch((error) => { + console.warn(error); + throw error; + }); + }, + }, // The additionalBody can contain: // - send_as_is: boolean (default: false) - If true, the invoice will be sent to E-conomic as is, without any modifications made by fixed pricing or vehicle subscriptions. invoice: async (invoiceId, additionalBody = { @@ -511,4 +608,4 @@ export const CollectedOrderInvoices = { ); } }; - \ No newline at end of file + diff --git a/src/components/viewport/page/headers/DesktopNavigationBuefy.vue b/src/components/viewport/page/headers/DesktopNavigationBuefy.vue index f8faed19..2dc6fb02 100644 --- a/src/components/viewport/page/headers/DesktopNavigationBuefy.vue +++ b/src/components/viewport/page/headers/DesktopNavigationBuefy.vue @@ -6,6 +6,7 @@ import { useNavigationItems } from "@/components/models/navigation/items/Navigat import {useRoute, useRouter} from "vue-router"; import LanguageSelector from "@/components/i18n/LanguageSelector.vue"; import { SessionUser } from '@/components/session/token/SessionUser.vue'; +import NavigationMenuGlobalSearch from "@/components/viewport/page/headers/menu/NavigationMenuGlobalSearch.vue"; const { parsedItems, parsedItemsGlobal } = useNavigationItems(); const VITE_BUILD_DATE = import.meta.env.VITE_BUILD_DATE || 'No build date'; const VITE_COMMIT_HASH = import.meta.env.VITE_COMMIT_HASH || 'No commit hash'; @@ -61,6 +62,9 @@ const visibleChildren = (item: any) => {