Add Goals module with CRUD functionality and OpenAPI documentation
This commit is contained in:
+252
-58
@@ -70,6 +70,8 @@ tags:
|
||||
description: Role and permission management
|
||||
- name: Self-Serve
|
||||
description: Self-serve lane operations and questions
|
||||
- name: Goals
|
||||
description: Department goals management
|
||||
|
||||
paths:
|
||||
# Authentication Endpoints
|
||||
@@ -2574,6 +2576,7 @@ paths:
|
||||
items:
|
||||
$ref: '#/components/schemas/Booking'
|
||||
|
||||
|
||||
/order-bookings:
|
||||
get:
|
||||
tags:
|
||||
@@ -2588,89 +2591,205 @@ paths:
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
|
||||
# Goals Endpoints
|
||||
/goals/department:
|
||||
get:
|
||||
tags:
|
||||
- Goals
|
||||
summary: List or get department goals
|
||||
description: |
|
||||
Retrieve a list of department goals or a single goal when `id` is provided.
|
||||
|
||||
Access control:
|
||||
- A user may only access goals where the goal's `departments` set is a subset of the user's departments.
|
||||
- Users with the `superuser` permission may access all goals.
|
||||
operationId: listDepartmentGoals
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
required: false
|
||||
schema: { type: integer }
|
||||
description: When provided, returns the single goal with this id (if accessible)
|
||||
- $ref: '#/components/parameters/PageParam'
|
||||
- $ref: '#/components/parameters/PerPageParam'
|
||||
- $ref: '#/components/parameters/SearchParam'
|
||||
- $ref: '#/components/parameters/FiltersParam'
|
||||
responses:
|
||||
'200':
|
||||
description: Goals retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DepartmentGoal'
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
post:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Create order booking
|
||||
operationId: createOrderBooking
|
||||
- Goals
|
||||
summary: Create department goal
|
||||
description: |
|
||||
Create a new department goal.
|
||||
|
||||
Access control:
|
||||
- The provided `departments` must be a subset of the user's departments unless the user has `superuser`.
|
||||
operationId: createDepartmentGoal
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [department, reg_1, datetime, items]
|
||||
properties:
|
||||
customer_number: {type: integer}
|
||||
department: {type: integer}
|
||||
reg_1: {type: string}
|
||||
reg_2: {type: string}
|
||||
reg_3: {type: string}
|
||||
datetime: {type: string, format: date-time}
|
||||
note: {type: string}
|
||||
reference: {type: string}
|
||||
po: {type: string}
|
||||
pickup: {type: boolean}
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, quantity]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
quantity: {type: integer}
|
||||
$ref: '#/components/schemas/DepartmentGoalCreate'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
'201':
|
||||
description: Department goal created successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DepartmentGoal'
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
put:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Update order booking
|
||||
operationId: updateOrderBooking
|
||||
- Goals
|
||||
summary: Update department goal
|
||||
description: |
|
||||
Update an existing department goal by `id`.
|
||||
|
||||
Access control:
|
||||
- The creator (`created_by`) may update regardless of department membership.
|
||||
- Otherwise the user must satisfy the same subset rule as for read access, and any new `departments` provided must also be a subset unless the user has `superuser`.
|
||||
operationId: updateDepartmentGoal
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
customer_number: {type: integer}
|
||||
department: {type: integer}
|
||||
reg_1: {type: string}
|
||||
reg_2: {type: string}
|
||||
reg_3: {type: string}
|
||||
datetime: {type: string, format: date-time}
|
||||
note: {type: string}
|
||||
reference: {type: string}
|
||||
po: {type: string}
|
||||
pickup: {type: boolean}
|
||||
order_id: {type: integer}
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, quantity]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
quantity: {type: integer}
|
||||
$ref: '#/components/schemas/DepartmentGoalUpdate'
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
description: Department goal updated successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DepartmentGoal'
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
delete:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Delete order booking
|
||||
operationId: deleteOrderBooking
|
||||
- Goals
|
||||
summary: Delete department goal
|
||||
description: |
|
||||
Delete a department goal by `id`.
|
||||
|
||||
Access control:
|
||||
- The creator (`created_by`) may delete regardless of department membership.
|
||||
- Otherwise the user must satisfy the subset rule or have `superuser`.
|
||||
operationId: deleteDepartmentGoal
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
required: true
|
||||
schema: {type: integer}
|
||||
schema: { type: integer }
|
||||
description: ID of the goal to delete
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
description: Department goal deleted successfully
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
'/order-bookings':
|
||||
post:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Create order booking
|
||||
operationId: createOrderBooking
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [department, reg_1, datetime, items]
|
||||
properties:
|
||||
customer_number: {type: integer}
|
||||
department: {type: integer}
|
||||
reg_1: {type: string}
|
||||
reg_2: {type: string}
|
||||
reg_3: {type: string}
|
||||
datetime: {type: string, format: date-time}
|
||||
note: {type: string}
|
||||
reference: {type: string}
|
||||
po: {type: string}
|
||||
pickup: {type: boolean}
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, quantity]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
quantity: {type: integer}
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
put:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Update order booking
|
||||
operationId: updateOrderBooking
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
customer_number: {type: integer}
|
||||
department: {type: integer}
|
||||
reg_1: {type: string}
|
||||
reg_2: {type: string}
|
||||
reg_3: {type: string}
|
||||
datetime: {type: string, format: date-time}
|
||||
note: {type: string}
|
||||
reference: {type: string}
|
||||
po: {type: string}
|
||||
pickup: {type: boolean}
|
||||
order_id: {type: integer}
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, quantity]
|
||||
properties:
|
||||
id: {type: integer}
|
||||
quantity: {type: integer}
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
delete:
|
||||
tags:
|
||||
- Bookings
|
||||
summary: Delete order booking
|
||||
operationId: deleteOrderBooking
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
required: true
|
||||
schema: {type: integer}
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
|
||||
/order-bookings/complete:
|
||||
post:
|
||||
@@ -5966,3 +6085,78 @@ components:
|
||||
description:
|
||||
type: string
|
||||
description: Human-readable description
|
||||
|
||||
GoalsCriteria:
|
||||
type: object
|
||||
description: Goal evaluation criteria
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: Criteria type (e.g., PRODUCT)
|
||||
example: PRODUCT
|
||||
target:
|
||||
type: number
|
||||
description: Target value for the goal
|
||||
example: 100
|
||||
start:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Start of the evaluation window (ISO 8601)
|
||||
end:
|
||||
type: string
|
||||
format: date-time
|
||||
description: End of the evaluation window (ISO 8601)
|
||||
users:
|
||||
type: array
|
||||
description: List of user customer numbers included in the criteria
|
||||
items: { type: integer }
|
||||
departments:
|
||||
type: array
|
||||
description: List of department IDs included in the criteria
|
||||
items: { type: integer }
|
||||
products:
|
||||
type: array
|
||||
description: List of product IDs included in the criteria
|
||||
items: { type: integer }
|
||||
|
||||
DepartmentGoal:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
created_by:
|
||||
type: integer
|
||||
description: ID of the user who created the goal
|
||||
departments:
|
||||
type: array
|
||||
items: { type: integer }
|
||||
criteria:
|
||||
$ref: '#/components/schemas/GoalsCriteria'
|
||||
created_at:
|
||||
type: string
|
||||
description: Creation timestamp
|
||||
updated_at:
|
||||
type: string
|
||||
description: Update timestamp
|
||||
|
||||
DepartmentGoalCreate:
|
||||
type: object
|
||||
required: [departments, criteria]
|
||||
properties:
|
||||
departments:
|
||||
type: array
|
||||
items: { type: integer }
|
||||
criteria:
|
||||
$ref: '#/components/schemas/GoalsCriteria'
|
||||
|
||||
DepartmentGoalUpdate:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
departments:
|
||||
type: array
|
||||
items: { type: integer }
|
||||
criteria:
|
||||
$ref: '#/components/schemas/GoalsCriteria'
|
||||
@@ -17,6 +17,7 @@ import {Roles} from "@/components/session/token/SessionUser/Objects/Roles.vue";
|
||||
import {Permissions} from "@/components/session/token/SessionUser/Objects/Permissions.vue";
|
||||
import {CollectedOrderInvoices} from "@/components/session/token/SessionUser/Objects/CollectedOrderInvoices.vue";
|
||||
import {DepartmentDailyReports} from "@/components/session/token/SessionUser/Objects/DepartmentDailyReports.vue";
|
||||
import {Goals} from "@/components/session/token/SessionUser/Objects/Goals.vue";
|
||||
import {Notifications} from "@/components/session/token/SessionUser/Objects/Notifications.vue";
|
||||
import {Bookings} from "@/components/session/token/SessionUser/Objects/Bookings.vue";
|
||||
import {Orders} from "@/components/session/token/SessionUser/Objects/Orders.vue";
|
||||
@@ -296,6 +297,7 @@ export const SessionUser = {
|
||||
permissions: Permissions,
|
||||
collectedOrderInvoices: CollectedOrderInvoices,
|
||||
department_daily_reports: DepartmentDailyReports,
|
||||
goals: Goals,
|
||||
notifications: Notifications,
|
||||
orders: Orders,
|
||||
forms: Forms,
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
<script>
|
||||
import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
|
||||
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
||||
|
||||
/**
|
||||
* The Goals object
|
||||
*/
|
||||
export const Goals = {
|
||||
meta: {
|
||||
title: "Mål",
|
||||
icon: "fas fa-bullseye",
|
||||
description: "Afdelingsmål",
|
||||
endpoint: "/goals/department",
|
||||
labels: {
|
||||
single: "mål",
|
||||
multiple: "mål"
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
label: "ID",
|
||||
type: "number",
|
||||
sortable: true,
|
||||
creation: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
created_by: {
|
||||
label: "Oprettet af",
|
||||
type: "number",
|
||||
sortable: true,
|
||||
creation: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
departments: {
|
||||
label: "Afdelinger",
|
||||
type: "array",
|
||||
sortable: true,
|
||||
creation: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
criteria: {
|
||||
label: "Kriterier",
|
||||
type: "object",
|
||||
sortable: false,
|
||||
creation: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
created_at: {
|
||||
label: "Oprettet",
|
||||
type: "date",
|
||||
sortable: true,
|
||||
creation: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
updated_at: {
|
||||
label: "Opdateret",
|
||||
type: "date",
|
||||
sortable: true,
|
||||
creation: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
},
|
||||
add: async (departments, criteria) => {
|
||||
return ObjectsGlobal.add.object(
|
||||
Goals.meta.endpoint,
|
||||
{
|
||||
departments: departments,
|
||||
criteria: criteria
|
||||
}
|
||||
);
|
||||
},
|
||||
set: {
|
||||
departments: async (id, departments) => {
|
||||
return ObjectsGlobal.set.column(
|
||||
Goals.meta.endpoint,
|
||||
id,
|
||||
"departments",
|
||||
departments
|
||||
)
|
||||
},
|
||||
criteria: async (id, criteria) => {
|
||||
return ObjectsGlobal.set.column(
|
||||
Goals.meta.endpoint,
|
||||
id,
|
||||
"criteria",
|
||||
criteria
|
||||
)
|
||||
},
|
||||
},
|
||||
get: {
|
||||
all: async () => {
|
||||
return ObjectsGlobal.get.objects(Goals.meta.endpoint);
|
||||
},
|
||||
single: async (id) => {
|
||||
return ObjectsGlobal.get.object(Goals.meta.endpoint, parseInt(id));
|
||||
}
|
||||
},
|
||||
update: async (id, departments, criteria) => {
|
||||
return authenticatedRequest(Goals.meta.endpoint, "PUT", {
|
||||
id: parseInt(id),
|
||||
departments: departments,
|
||||
criteria: criteria
|
||||
});
|
||||
},
|
||||
delete: async (id) => {
|
||||
return ObjectsGlobal.delete.object(Goals.meta.endpoint, parseInt(id));
|
||||
},
|
||||
functions: {
|
||||
showDeleteObjectForm: (id, onAfterSubmit = null) => {
|
||||
return ObjectsGlobal.showDeleteObjectForm(Goals, id, onAfterSubmit);
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Show the create object form
|
||||
* @param onAfterSubmit
|
||||
* @param lockedValues
|
||||
* @returns {Promise<SweetAlertResult<Awaited<any>>>}
|
||||
*/
|
||||
showCreateObjectForm: (onAfterSubmit = null, lockedValues = {}) => {
|
||||
return ObjectsGlobal.showCreateObjectForm(Goals, onAfterSubmit, lockedValues);
|
||||
},
|
||||
/**
|
||||
* Show the edit object field form
|
||||
* @param id
|
||||
* @param column
|
||||
* @param value
|
||||
* @param onAfterSubmit
|
||||
* @returns {Promise<SweetAlertResult<Awaited<any>>>}
|
||||
*/
|
||||
showEditObjectFieldForm: (id, column, value, onAfterSubmit = null) => {
|
||||
return ObjectsGlobal.showEditObjectFieldForm(
|
||||
Goals,
|
||||
id,
|
||||
column,
|
||||
value,
|
||||
onAfterSubmit
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user