The nesting module serves nesting jobs with approval, costing, material reservation, export and result import, part pulls, summaries, and SVG and DXF output, plus nesting parts, remnants, and a KPI read. All 27 endpoints sit under /api/v1/nesting. For business meaning see the Nesting module page.
Group facts¶
| Item | Value | Source |
|---|---|---|
| Base path | /api/v1/nesting |
src/cluster_erp/core/module_base.py:37-38, src/cluster_erp/modules/nesting/module.py:49 |
| Routers | 4 router objects in 4 files; listed in the section Endpoint summary | src/cluster_erp/modules/nesting/module.py:32 |
| Endpoints | 27, counted from the route decorators and add_api_route calls of the routers that register_routes mounts |
src/cluster_erp/modules/nesting/kpi/routes.py, src/cluster_erp/modules/nesting/nesting_jobs/routes.py, src/cluster_erp/modules/nesting/nesting_parts/routes.py ... |
| Authentication | Bearer header or session cookie, resolved by get_current_user; see Authentication and headers |
src/cluster_erp/modules/auth/guards.py:85-167 |
| Permission guard | 27 of 27 routes declare require_permission |
src/cluster_erp/modules/auth/guards.py:205-240 |
| App entitlement gate | require_app("nesting") on every mounted router |
src/cluster_erp/modules/nesting/module.py:55 |
| Error format | JSON body {"error": {...}}; see Errors |
src/cluster_erp/server/error_handler.py:33-46 |
Endpoint summary¶
One row per sub-module folder. The router prefix is added to the base path.
| Sub-module | Routers | Endpoints | Router source |
|---|---|---|---|
kpi |
router (/nesting-jobs) |
1 | src/cluster_erp/modules/nesting/kpi/routes.py:13 |
nesting_jobs |
router (/nesting-jobs) |
15 | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:30 |
nesting_parts |
router (/nesting-parts) |
6 | src/cluster_erp/modules/nesting/nesting_parts/routes.py:25 |
nesting_remnants |
router (/nesting-remnants) |
5 | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:27 |
Nesting KPI endpoints¶
One KPI read under /nesting-jobs/kpi.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/nesting/nesting-jobs/kpi |
get_nesting_kpi |
nesting_job.read |
none | period_start, period_end, granularity, material_grade |
NestingKpiResponse |
200 (default) | src/cluster_erp/modules/nesting/kpi/routes.py:16 |
Nesting jobs endpoints¶
Job records, approve, calculate costs, reserve material, export, import a result, pull parts from orders, summary, SVG, and DXF.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/nesting/nesting-jobs/ |
list_nesting_jobs |
nesting_job.read |
none | search, status, material_grade, limit, offset |
dict |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:121 |
GET |
/api/v1/nesting/nesting-jobs/{job_id} |
get_nesting_job |
nesting_job.read |
none | none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:147 |
POST |
/api/v1/nesting/nesting-jobs/ |
create_nesting_job |
nesting_job.create |
NestingJobCreate |
none | NestingJobRead |
201 |
src/cluster_erp/modules/nesting/nesting_jobs/routes.py:161 |
PUT |
/api/v1/nesting/nesting-jobs/{job_id} |
update_nesting_job |
nesting_job.update |
NestingJobUpdate |
none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:173 |
DELETE |
/api/v1/nesting/nesting-jobs/{job_id} |
delete_nesting_job |
nesting_job.delete |
none | none | none | 204 |
src/cluster_erp/modules/nesting/nesting_jobs/routes.py:188 |
POST |
/api/v1/nesting/nesting-jobs/bulk-delete |
bulk_delete_nesting_jobs |
nesting_job.bulk_delete |
BulkDeleteRequest |
none | BulkDeleteResponse |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:201 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/approve |
approve_nesting_job |
nesting_job.approve |
none | none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:213 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/calculate-costs |
calculate_costs |
nesting_job.update |
none | none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:227 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/reserve-material |
reserve_material |
nesting_job.update |
none | none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:239 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/export |
export_nesting_job |
nesting_job.read |
none | none | dict[str, Any] |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:251 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/import-result |
import_nesting_result |
nesting_job.run |
NestingImportResult |
none | NestingJobRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:267 |
POST |
/api/v1/nesting/nesting-jobs/{job_id}/pull-parts |
pull_parts_from_orders |
nesting_job.update |
none | none | list[NestingPartRead] |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:284 |
GET |
/api/v1/nesting/nesting-jobs/{job_id}/summary |
get_nesting_summary |
nesting_job.read |
none | none | dict |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:296 |
GET |
/api/v1/nesting/nesting-jobs/{job_id}/svg |
get_nesting_svg |
nesting_job.read |
none | sheet_index |
PlainTextResponse |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:307 |
GET |
/api/v1/nesting/nesting-jobs/{job_id}/dxf |
get_nesting_dxf |
nesting_job.read |
none | none | Response |
200 (default) | src/cluster_erp/modules/nesting/nesting_jobs/routes.py:364 |
Nesting parts endpoints¶
Nesting part records.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/nesting/nesting-parts/ |
list_nesting_parts |
nesting_part.read |
none | nesting_job_id, limit, offset |
dict |
200 (default) | src/cluster_erp/modules/nesting/nesting_parts/routes.py:28 |
GET |
/api/v1/nesting/nesting-parts/{part_id} |
get_nesting_part |
nesting_part.read |
none | none | NestingPartRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_parts/routes.py:50 |
POST |
/api/v1/nesting/nesting-parts/ |
create_nesting_part |
nesting_part.create |
NestingPartCreate |
none | NestingPartRead |
201 |
src/cluster_erp/modules/nesting/nesting_parts/routes.py:64 |
PUT |
/api/v1/nesting/nesting-parts/{part_id} |
update_nesting_part |
nesting_part.update |
NestingPartUpdate |
none | NestingPartRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_parts/routes.py:76 |
DELETE |
/api/v1/nesting/nesting-parts/{part_id} |
delete_nesting_part |
nesting_part.delete |
none | none | none | 204 |
src/cluster_erp/modules/nesting/nesting_parts/routes.py:91 |
POST |
/api/v1/nesting/nesting-parts/bulk-delete |
bulk_delete_nesting_parts |
nesting_part.bulk_delete |
BulkDeleteRequest |
none | BulkDeleteResponse |
200 (default) | src/cluster_erp/modules/nesting/nesting_parts/routes.py:104 |
Remnants endpoints¶
Remnant records, available remnants, and scrap.
| Method | Path | Handler | Permission | Request | Query | Response | Status | Source |
|---|---|---|---|---|---|---|---|---|
GET |
/api/v1/nesting/nesting-remnants/ |
list_nesting_remnants |
nesting_remnant.read |
none | nesting_job_id, status, material_grade, limit, offset |
dict |
200 (default) | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:30 |
GET |
/api/v1/nesting/nesting-remnants/available |
get_available_remnants |
nesting_remnant.read |
none | material_grade, thickness_mm, min_width, min_height, limit, offset |
dict |
200 (default) | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:56 |
GET |
/api/v1/nesting/nesting-remnants/{remnant_id} |
get_nesting_remnant |
nesting_remnant.read |
none | none | NestingRemnantRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:84 |
PUT |
/api/v1/nesting/nesting-remnants/{remnant_id} |
update_nesting_remnant |
nesting_remnant.update |
NestingRemnantUpdate |
none | NestingRemnantRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:98 |
POST |
/api/v1/nesting/nesting-remnants/{remnant_id}/scrap |
scrap_nesting_remnant |
nesting_remnant.update |
NestingRemnantScrap |
none | NestingRemnantRead |
200 (default) | src/cluster_erp/modules/nesting/nesting_remnants/routes.py:113 |
Error codes¶
The table lists the error codes that code under src/cluster_erp/modules/nesting/ raises, found by a syntax-tree scan for AppError(...), the factories in src/cluster_erp/core/errors.py (not_found, conflict, protected, permission_denied, privilege_escalation, check_version), and HTTPException(...). A status of 400 is the AppError default (src/cluster_erp/core/errors.py:23). Guard errors (401, 403), the entitlement codes, and validation errors (422) apply to every route and are listed on Errors.
| Code | HTTP status | Raised in | First site |
|---|---|---|---|
CONCURRENCY_VERSION_CONFLICT |
409 |
nesting_jobs, nesting_parts, nesting_remnants |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:200 |
NESTING_ALREADY_APPROVED |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:327 |
NESTING_COST_MISSING_UNIT_PRICE |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:589 |
NESTING_IMPORT_FAILED |
400 |
integration |
src/cluster_erp/modules/nesting/integration/services.py:345 |
NESTING_IMPORT_INVALID_PARTS |
400 |
integration |
src/cluster_erp/modules/nesting/integration/services.py:91 |
NESTING_INVALID_STATUS |
400 |
integration, nesting_jobs, nesting_parts |
src/cluster_erp/modules/nesting/integration/services.py:179 |
NESTING_LOW_UTILIZATION |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:337 |
NESTING_MATERIAL_ALREADY_CONSUMED |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:476 |
NESTING_MATERIAL_ALREADY_RESERVED |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:410 |
NESTING_MATERIAL_NOT_RESERVED |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:482 |
NESTING_NOT_COMPLETED |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:321 |
NESTING_NO_PARTS |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:692 |
NESTING_ORDER_MATERIAL_MISMATCH |
400 |
nesting_jobs |
src/cluster_erp/modules/nesting/nesting_jobs/services.py:731 |
NESTING_PART_TOO_LARGE |
400 |
nesting_parts |
src/cluster_erp/modules/nesting/nesting_parts/services.py:100 |
NESTING_REMNANT_INVALID_STATUS |
400 |
nesting_remnants |
src/cluster_erp/modules/nesting/nesting_remnants/services.py:189 |
RESOURCE_CONFLICT |
400 |
nesting_parts |
src/cluster_erp/modules/nesting/nesting_parts/services.py:89 |
RESOURCE_NOT_FOUND |
404 |
integration, nesting_jobs, nesting_parts, nesting_remnants |
src/cluster_erp/modules/nesting/integration/services.py:119 |
Events emitted¶
The table lists the contract classes that code under src/cluster_erp/modules/nesting/ passes directly to EventBus.emit, found by a syntax-tree scan. Events built in a variable first are not counted. The contracts are described in Contracts reference.
| Contract | Sites | First site |
|---|---|---|
NestingApproved |
1 | src/cluster_erp/modules/nesting/nesting_jobs/services.py:353 |
NestingCompleted |
1 | src/cluster_erp/modules/nesting/integration/services.py:352 |
NestingCostCalculated |
1 | src/cluster_erp/modules/nesting/nesting_jobs/services.py:661 |
NestingMaterialConsumed |
1 | src/cluster_erp/modules/nesting/nesting_jobs/services.py:544 |
NestingMaterialReserved |
1 | src/cluster_erp/modules/nesting/nesting_jobs/services.py:458 |
NestingRemnantConsumed |
1 | src/cluster_erp/modules/nesting/nesting_remnants/services.py:236 |
NestingRemnantCreated |
1 | src/cluster_erp/modules/nesting/nesting_remnants/services.py:166 |
NestingRemnantScrapped |
1 | src/cluster_erp/modules/nesting/nesting_remnants/services.py:272 |
NestingScrapRecorded |
1 | src/cluster_erp/modules/nesting/nesting_jobs/services.py:380 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.