From f3d2951dc366744ae06720f196153451078009b9 Mon Sep 17 00:00:00 2001 From: chenjw28 <792430652@qq.com> Date: Mon, 20 Jul 2026 10:16:27 +0800 Subject: [PATCH] x --- docs/EVOLUTION_ROADMAP.md | 18 +- frontend/package-lock.json | 350 +- frontend/package.json | 4 +- .../inventory/components/FinanceTab.vue | 12 +- .../inventory/composables/useInventory.ts | 21 +- frontend/src/types/api.ts | 4605 +++++++++ frontend/src/types/schemas.ts | 11 + openapi-inventory.json | 8169 +++++++++++++++ openapi.json | 8777 +++++++++++++++++ src/inventory/api/inventory_routes.py | 167 +- src/inventory/api/purchase_order_routes.py | 407 +- src/inventory/api/sales_order_routes.py | 695 +- src/inventory/api/stock_movement_routes.py | 188 +- src/inventory/api/utils.py | 22 - src/inventory/schemas/__init__.py | 4 +- src/inventory/schemas/sales_order_schemas.py | 10 + src/inventory/services/inventory_service.py | 180 + .../services/purchase_order_service.py | 454 + src/inventory/services/sales_order_service.py | 744 ++ .../services/stock_movement_service.py | 193 + src/moldinsight/core/aluminum_foam_mold.py | 59 +- src/moldinsight/core/geometry_analyzer.py | 12 +- src/moldinsight/core/mold_generator.py | 16 +- src/moldinsight/services/material_service.py | 51 +- 24 files changed, 23623 insertions(+), 1546 deletions(-) create mode 100644 frontend/src/types/api.ts create mode 100644 frontend/src/types/schemas.ts create mode 100644 openapi-inventory.json create mode 100644 openapi.json delete mode 100644 src/inventory/api/utils.py create mode 100644 src/inventory/services/inventory_service.py create mode 100644 src/inventory/services/purchase_order_service.py create mode 100644 src/inventory/services/sales_order_service.py create mode 100644 src/inventory/services/stock_movement_service.py diff --git a/docs/EVOLUTION_ROADMAP.md b/docs/EVOLUTION_ROADMAP.md index 07dfe66..00fd59c 100644 --- a/docs/EVOLUTION_ROADMAP.md +++ b/docs/EVOLUTION_ROADMAP.md @@ -108,17 +108,19 @@ ### P1-5 统一材料属性源 - **现状**:材料字典在 4 处重复定义且冲突(PE 收缩率 `material_service` 0.020 vs `aluminum_foam_mold.py:92` 0.025)。 - **目标**:`MaterialService` 作为唯一源,其他模块查询。 -- **状态**:- [ ] +- **状态**:- [x] --- -### P1 执行结果(进行中) +### P1 执行结果(核心完成) -- 🚧 **P1-1 进销存抽 service 层**(进行中): - - ✅ 建立 `inventory/services/` 层,抽出 `FinanceService`(`finance_routes` 767→123 行,业务逻辑全下沉;路由仅做参数校验+响应组装) - - ✅ 将 `schemas/` 与 `utils.py` 从 `inventory/api/` 移至 `inventory/` 顶层,打破 service↔api 循环导入(schemas 是共享 DTO、utils 是纯函数,本不应嵌在 api 层;这是正确分层) - - ✅ import 测试通过:9 finance 路由 + 55 inventory 路由全部正常加载 - - ⏳ 待办:抽 `sales_order_service` / `purchase_order_service` / `inventory`+`stock_movement` service;清理死代码 `api/utils.py`(待 `git rm`) +- ✅ **P1-1 进销存抽 service 层**(核心完成): + - 建立 `inventory/services/` 层,抽出 5 个 service:FinanceService / SalesOrderService / PurchaseOrderService / StockMovementService / InventoryService + - 路由全面瘦身:finance 767->123、sales_order 777->114、purchase_order 472->90、stock_movement 209->37、inventory 205->57 + - 将 schemas/ 与 utils.py 从 inventory/api/ 移至 inventory/ 顶层,打破 service<->api 循环导入(正确分层);清理死代码 api/utils.py + - ✅ 全量 import 测试通过:55 inventory 路由无丢失,5 个 service 全部正常加载 + - ⏳ 可选后续:剩余纯 CRUD 路由(product/supplier/customer/warehouse/material/dashboard)体量小,可按需增量抽取 +- ✅ **P1-5 统一材料属性源**:`MaterialService` 成为唯一源,删除 geometry_analyzer/mold_generator/aluminum_foam_mold 三处重复字典,改查询 MaterialService;解决冲突(PE 收缩率统一 0.020、PC/PA/PMMA 收缩率、POM 密度统一)、补齐 PS、统一泡沫 `shrinkage` 键名、补 `min_wall`/泡沫字段;py_compile + 一致性核对通过 --- @@ -157,6 +159,6 @@ | 阶段 | 项数 | 已完成 | 进行中 | |------|------|--------|--------| | P0 | 7 | 6 修复 + 1 排查 | - | -| P1 | 5 | 0 | P1-1 进行中(finance 已抽) | +| P1 | 5 | 2 | P1-1 核心完成 + P1-5 完成 | | P2 | 3 | 0 | - | | P3 | 7 | 0 | - | diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 67306c4..1e229ce 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,11 +20,27 @@ "@types/node": "^24.12.3", "@vitejs/plugin-vue": "^6.0.6", "@vue/tsconfig": "^0.9.1", + "openapi-typescript": "^7.13.0", "typescript": "~6.0.2", "vite": "^8.0.12", "vue-tsc": "^3.2.8" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -166,6 +182,52 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.17", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.17.tgz", + "integrity": "sha512-wsV2keCt6B806XpSdezbWZ9aFJYf14YVh+XQf0ESt7M90yqVuxH9//PxvtC70sgj9OCkRM3nRaLfu4MsGQZRig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.2.0", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", @@ -716,6 +778,16 @@ } } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/air-datepicker": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.6.0.tgz", @@ -729,6 +801,30 @@ "dev": true, "license": "MIT" }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/birpc": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", @@ -738,6 +834,23 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/chart.js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", @@ -750,6 +863,13 @@ "pnpm": ">=8" } }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, "node_modules/copy-anything": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", @@ -777,6 +897,24 @@ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -805,6 +943,13 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -844,6 +989,33 @@ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "license": "MIT" }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-what": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", @@ -856,6 +1028,53 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -1144,12 +1363,32 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", @@ -1175,6 +1414,45 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -1228,6 +1506,16 @@ } } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -1256,6 +1544,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -1332,6 +1630,19 @@ "node": ">=16" } }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/tdesign-icons-vue-next": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/tdesign-icons-vue-next/-/tdesign-icons-vue-next-0.4.4.tgz", @@ -1402,11 +1713,24 @@ "license": "0BSD", "optional": true }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -1423,6 +1747,13 @@ "dev": true, "license": "MIT" }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, "node_modules/validator": { "version": "13.15.35", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", @@ -1575,6 +1906,23 @@ "peerDependencies": { "typescript": ">=5.0.0" } + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } } } } diff --git a/frontend/package.json b/frontend/package.json index 3984150..9356919 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "vite", "build": "vue-tsc -b && vite build", - "preview": "vite preview" + "preview": "vite preview", + "gen:api": "openapi-typescript ../openapi-inventory.json -o src/types/api.ts" }, "dependencies": { "air-datepicker": "^3.6.0", @@ -21,6 +22,7 @@ "@types/node": "^24.12.3", "@vitejs/plugin-vue": "^6.0.6", "@vue/tsconfig": "^0.9.1", + "openapi-typescript": "^7.13.0", "typescript": "~6.0.2", "vite": "^8.0.12", "vue-tsc": "^3.2.8" diff --git a/frontend/src/modules/inventory/components/FinanceTab.vue b/frontend/src/modules/inventory/components/FinanceTab.vue index 0ee2ef9..5c8c924 100644 --- a/frontend/src/modules/inventory/components/FinanceTab.vue +++ b/frontend/src/modules/inventory/components/FinanceTab.vue @@ -37,22 +37,22 @@ onMounted(() => { refreshFinance() }) -// 字段名对齐后端 FinanceSummaryResponse: -// receivable_total / payable_total / period_receipt_total / period_payment_total +// 字段名对齐后端 FinanceSummaryResponse。 +// 后端 Decimal 序列化为 JSON 字符串,故用 Number() 转换再填充 0。 const totalReceivable = computed(() => { - return state.financeSummary?.receivable_total ?? 0 + return Number(state.financeSummary?.receivable_total) || 0 }) const totalPayable = computed(() => { - return state.financeSummary?.payable_total ?? 0 + return Number(state.financeSummary?.payable_total) || 0 }) const periodReceived = computed(() => { - return state.financeSummary?.period_receipt_total ?? 0 + return Number(state.financeSummary?.period_receipt_total) || 0 }) const periodPaid = computed(() => { - return state.financeSummary?.period_payment_total ?? 0 + return Number(state.financeSummary?.period_payment_total) || 0 }) function getTransactionTypeLabel(type: string): string { diff --git a/frontend/src/modules/inventory/composables/useInventory.ts b/frontend/src/modules/inventory/composables/useInventory.ts index 9f831a8..32ff17a 100644 --- a/frontend/src/modules/inventory/composables/useInventory.ts +++ b/frontend/src/modules/inventory/composables/useInventory.ts @@ -2,6 +2,7 @@ import { reactive, ref, computed } from 'vue' import { apiRequest } from '@/shared/api' import { addNotification, handleApiError } from '@/shared/notification' import { formatCurrency, formatNumber, formatDateTime, formatDate } from '@/shared/utils' +import type { Schema } from '@/types/schemas' declare const AirDatepicker: any @@ -10,19 +11,19 @@ const state = reactive({ backendDbReady: true, backendDbMessage: '' as string, productCategory: 'finished' as string, - dashboard: null as any, - financeSummary: null as any, + dashboard: null as any, // 后端 /api/dashboard 无 response_model,暂未生成类型 + financeSummary: null as Schema<'FinanceSummaryResponse'> | null, financePeriod: { year: new Date().getFullYear(), quarter: '' as string }, - financeTransactions: [] as any[], - receivables: [] as any[], - payables: [] as any[], - customerFinanceStatement: [] as any[], - supplierFinanceStatement: [] as any[], - customerProductStatement: [] as any[], - supplierProductStatement: [] as any[], + financeTransactions: [] as Schema<'FinanceTransactionResponse'>[], + receivables: [] as Schema<'ReceivableItemResponse'>[], + payables: [] as Schema<'PayableItemResponse'>[], + customerFinanceStatement: [] as Schema<'PartnerStatementItemResponse'>[], + supplierFinanceStatement: [] as Schema<'PartnerStatementItemResponse'>[], + customerProductStatement: [] as Schema<'PartnerProductStatementItemResponse'>[], + supplierProductStatement: [] as Schema<'PartnerProductStatementItemResponse'>[], products: [] as any[], materials: [] as any[], finishedProducts: [] as any[], @@ -392,7 +393,7 @@ export function useInventory() { apiRequest(`/api/finance/partner-product-statement/supplier?${periodQuery}`) ]) state.financeSummary = summary - state.financeTransactions = transactions + state.financeTransactions = transactions?.items || [] state.receivables = receivables state.payables = payables state.customerFinanceStatement = customerStatement.items || [] diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts new file mode 100644 index 0000000..65fc48e --- /dev/null +++ b/frontend/src/types/api.ts @@ -0,0 +1,4605 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/api/auth/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Login */ + post: operations["login_api_auth_login_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/login/json": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Login Json */ + post: operations["login_json_api_auth_login_json_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Current User Info */ + get: operations["get_current_user_info_api_auth_me_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Logout */ + post: operations["logout_api_auth_logout_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/users": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Users */ + get: operations["list_users_api_auth_users_get"]; + put?: never; + /** Create User */ + post: operations["create_user_api_auth_users_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/users/{user_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update User */ + put: operations["update_user_api_auth_users__user_id__put"]; + post?: never; + /** Delete User */ + delete: operations["delete_user_api_auth_users__user_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/users/{user_id}/reset-password": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Reset User Password */ + put: operations["reset_user_password_api_auth_users__user_id__reset_password_put"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/roles": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Roles */ + get: operations["list_roles_api_auth_roles_get"]; + put?: never; + /** Create Role */ + post: operations["create_role_api_auth_roles_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/roles/{role_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Role */ + put: operations["update_role_api_auth_roles__role_id__put"]; + post?: never; + /** Delete Role */ + delete: operations["delete_role_api_auth_roles__role_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/roles/{role_id}/permissions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Set Role Permissions */ + put: operations["set_role_permissions_api_auth_roles__role_id__permissions_put"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/permissions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Permissions */ + get: operations["list_permissions_api_auth_permissions_get"]; + put?: never; + /** Create Permission */ + post: operations["create_permission_api_auth_permissions_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/auth/permissions/{permission_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete Permission */ + delete: operations["delete_permission_api_auth_permissions__permission_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/products": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Products */ + get: operations["list_products_api_products_get"]; + put?: never; + /** Create Product */ + post: operations["create_product_api_products_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/products/{product_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Product */ + put: operations["update_product_api_products__product_id__put"]; + post?: never; + /** Delete Product */ + delete: operations["delete_product_api_products__product_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/products/{product_id}/materials": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Product Bom */ + get: operations["get_product_bom_api_products__product_id__materials_get"]; + /** Replace Product Bom */ + put: operations["replace_product_bom_api_products__product_id__materials_put"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/suppliers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Suppliers */ + get: operations["list_suppliers_api_suppliers_get"]; + put?: never; + /** Create Supplier */ + post: operations["create_supplier_api_suppliers_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/suppliers/{supplier_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Supplier */ + put: operations["update_supplier_api_suppliers__supplier_id__put"]; + post?: never; + /** Delete Supplier */ + delete: operations["delete_supplier_api_suppliers__supplier_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/customers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Customers */ + get: operations["list_customers_api_customers_get"]; + put?: never; + /** Create Customer */ + post: operations["create_customer_api_customers_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/customers/{customer_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Customer */ + put: operations["update_customer_api_customers__customer_id__put"]; + post?: never; + /** Delete Customer */ + delete: operations["delete_customer_api_customers__customer_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/warehouses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Warehouses */ + get: operations["list_warehouses_api_warehouses_get"]; + put?: never; + /** Create Warehouse */ + post: operations["create_warehouse_api_warehouses_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/inventory": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Inventory */ + get: operations["list_inventory_api_inventory_get"]; + put?: never; + /** Create Inventory */ + post: operations["create_inventory_api_inventory_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/inventory/{inventory_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Update Inventory */ + put: operations["update_inventory_api_inventory__inventory_id__put"]; + post?: never; + /** Delete Inventory */ + delete: operations["delete_inventory_api_inventory__inventory_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/stock-movements": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Stock Movements */ + get: operations["list_stock_movements_api_stock_movements_get"]; + put?: never; + /** Create Stock Movement */ + post: operations["create_stock_movement_api_stock_movements_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/purchase-orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Purchase Orders */ + get: operations["list_purchase_orders_api_purchase_orders_get"]; + put?: never; + /** Create Purchase Order */ + post: operations["create_purchase_order_api_purchase_orders_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/purchase-orders/{order_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Purchase Order Detail */ + get: operations["get_purchase_order_detail_api_purchase_orders__order_id__get"]; + /** Update Purchase Order */ + put: operations["update_purchase_order_api_purchase_orders__order_id__put"]; + post?: never; + /** Delete Purchase Order */ + delete: operations["delete_purchase_order_api_purchase_orders__order_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/purchase-orders/{order_id}/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** Update Purchase Order Status */ + patch: operations["update_purchase_order_status_api_purchase_orders__order_id__status_patch"]; + trace?: never; + }; + "/api/purchase-orders/{order_id}/receive": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Receive Purchase Order */ + post: operations["receive_purchase_order_api_purchase_orders__order_id__receive_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sales-orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Sales Orders */ + get: operations["list_sales_orders_api_sales_orders_get"]; + put?: never; + /** Create Sales Order */ + post: operations["create_sales_order_api_sales_orders_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sales-orders/{order_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Sales Order Detail */ + get: operations["get_sales_order_detail_api_sales_orders__order_id__get"]; + /** Update Sales Order */ + put: operations["update_sales_order_api_sales_orders__order_id__put"]; + post?: never; + /** Delete Sales Order */ + delete: operations["delete_sales_order_api_sales_orders__order_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sales-orders/{order_id}/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** Update Sales Order Status */ + patch: operations["update_sales_order_status_api_sales_orders__order_id__status_patch"]; + trace?: never; + }; + "/api/sales-orders/{order_id}/consume-materials": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Consume Materials + * @description 记录销售订单的物料消耗(在已发料基础上记录额外消耗,如报废/超耗) + */ + post: operations["consume_materials_api_sales_orders__order_id__consume_materials_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sales-orders/{order_id}/production-plan": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Sales Order Production Plan */ + get: operations["get_sales_order_production_plan_api_sales_orders__order_id__production_plan_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/sales-orders/{order_id}/issue-materials": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Issue Sales Order Materials */ + post: operations["issue_sales_order_materials_api_sales_orders__order_id__issue_materials_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/materials/{product_id}/price-history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Material Price History */ + get: operations["get_material_price_history_api_materials__product_id__price_history_get"]; + put?: never; + /** Add Material Price History */ + post: operations["add_material_price_history_api_materials__product_id__price_history_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/materials/{product_id}/price-trend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Material Price Trend */ + get: operations["get_material_price_trend_api_materials__product_id__price_trend_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/materials/{product_id}/suppliers": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Material Suppliers */ + get: operations["get_material_suppliers_api_materials__product_id__suppliers_get"]; + put?: never; + /** Add Material Supplier */ + post: operations["add_material_supplier_api_materials__product_id__suppliers_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/materials/suppliers/{supplier_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Remove Material Supplier */ + delete: operations["remove_material_supplier_api_materials_suppliers__supplier_id__delete"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/materials/suppliers/{supplier_id}/materials": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Supplier Materials */ + get: operations["get_supplier_materials_api_materials_suppliers__supplier_id__materials_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/dashboard": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Dashboard */ + get: operations["get_dashboard_api_dashboard_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/receipts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Create Receipt */ + post: operations["create_receipt_api_finance_receipts_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/payments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Create Payment */ + post: operations["create_payment_api_finance_payments_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/transactions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Transactions */ + get: operations["list_transactions_api_finance_transactions_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/transactions/{transaction_id}/void": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Void Transaction */ + post: operations["void_transaction_api_finance_transactions__transaction_id__void_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/receivables": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Receivables */ + get: operations["list_receivables_api_finance_receivables_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/payables": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List Payables */ + get: operations["list_payables_api_finance_payables_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/summary": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Finance Summary */ + get: operations["get_finance_summary_api_finance_summary_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/partner-statement/{partner_type}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Partner Statement */ + get: operations["get_partner_statement_api_finance_partner_statement__partner_type__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/finance/partner-product-statement/{partner_type}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get Partner Product Statement */ + get: operations["get_partner_product_statement_api_finance_partner_product_statement__partner_type__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Health */ + get: operations["health_health_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/{full_path}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Spa Fallback */ + get: operations["spa_fallback__full_path__get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** Body_login_api_auth_login_post */ + Body_login_api_auth_login_post: { + /** Grant Type */ + grant_type?: string | null; + /** Username */ + username: string; + /** + * Password + * Format: password + */ + password: string; + /** + * Scope + * @default + */ + scope: string; + /** Client Id */ + client_id?: string | null; + /** + * Client Secret + * Format: password + */ + client_secret?: string | null; + }; + /** CustomerCreate */ + CustomerCreate: { + /** Code */ + code?: string | null; + /** Name */ + name: string; + /** Contact Person */ + contact_person?: string | null; + /** Phone */ + phone?: string | null; + /** Email */ + email?: string | null; + /** Address */ + address?: string | null; + }; + /** CustomerResponse */ + CustomerResponse: { + /** Id */ + id: number; + /** Code */ + code: string | null; + /** Name */ + name: string; + /** Contact Person */ + contact_person: string | null; + /** Phone */ + phone: string | null; + /** Email */ + email: string | null; + /** Is Active */ + is_active: boolean; + }; + /** FinanceAllocationCreate */ + FinanceAllocationCreate: { + /** + * Order Type + * @enum {string} + */ + order_type: "sales" | "purchase"; + /** Order Id */ + order_id: number; + /** Allocated Amount */ + allocated_amount: number | string; + }; + /** FinanceAllocationResponse */ + FinanceAllocationResponse: { + /** Id */ + id: number; + /** Order Type */ + order_type: string; + /** Order Id */ + order_id: number; + /** Allocated Amount */ + allocated_amount: string; + }; + /** FinancePartnerProductStatementResponse */ + FinancePartnerProductStatementResponse: { + /** + * Partner Type + * @enum {string} + */ + partner_type: "customer" | "supplier"; + /** Year */ + year: number; + /** Quarter */ + quarter?: number | null; + /** Period Label */ + period_label: string; + /** Partner Id */ + partner_id?: number | null; + /** Order Amount Total */ + order_amount_total: string; + /** Settled Amount Total */ + settled_amount_total: string; + /** Outstanding Amount Total */ + outstanding_amount_total: string; + /** + * Items + * @default [] + */ + items: components["schemas"]["PartnerProductStatementItemResponse"][]; + }; + /** FinancePartnerStatementResponse */ + FinancePartnerStatementResponse: { + /** + * Partner Type + * @enum {string} + */ + partner_type: "customer" | "supplier"; + /** Year */ + year: number; + /** Quarter */ + quarter?: number | null; + /** Period Label */ + period_label: string; + /** Order Total */ + order_total: string; + /** Settled Total */ + settled_total: string; + /** Transaction Total */ + transaction_total: string; + /** Outstanding Total */ + outstanding_total: string; + /** + * Items + * @default [] + */ + items: components["schemas"]["PartnerStatementItemResponse"][]; + }; + /** FinanceSummaryResponse */ + FinanceSummaryResponse: { + /** Receivable Total */ + receivable_total: string; + /** Payable Total */ + payable_total: string; + /** Monthly Receipt Total */ + monthly_receipt_total: string; + /** Monthly Payment Total */ + monthly_payment_total: string; + /** Selected Year */ + selected_year: number; + /** Selected Quarter */ + selected_quarter?: number | null; + /** Period Label */ + period_label: string; + /** + * Period Receipt Total + * @default 0 + */ + period_receipt_total: string; + /** + * Period Payment Total + * @default 0 + */ + period_payment_total: string; + /** + * Overdue Receivable Count + * @default 0 + */ + overdue_receivable_count: number; + /** + * Overdue Payable Count + * @default 0 + */ + overdue_payable_count: number; + }; + /** FinanceTransactionResponse */ + FinanceTransactionResponse: { + /** Id */ + id: number; + /** Txn No */ + txn_no: string; + /** + * Txn Type + * @enum {string} + */ + txn_type: "receipt" | "payment"; + /** + * Partner Type + * @enum {string} + */ + partner_type: "customer" | "supplier"; + /** Partner Id */ + partner_id: number; + /** Partner Name */ + partner_name?: string | null; + /** Amount */ + amount: string; + /** + * Txn Date + * Format: date-time + */ + txn_date: string; + /** Method */ + method: string; + /** Account Name */ + account_name: string | null; + /** + * Status + * @enum {string} + */ + status: "confirmed" | "voided"; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Allocations + * @default [] + */ + allocations: components["schemas"]["FinanceAllocationResponse"][]; + }; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** InventoryCreate */ + InventoryCreate: { + /** Product Id */ + product_id: number; + /** Warehouse Id */ + warehouse_id: number; + /** + * Quantity + * @default 0 + */ + quantity: number | string; + /** + * Locked Quantity + * @default 0 + */ + locked_quantity: number | string; + /** Batch Number */ + batch_number?: string | null; + /** Location */ + location?: string | null; + }; + /** InventoryResponse */ + InventoryResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Product Name */ + product_name: string; + /** Product Sku */ + product_sku: string; + /** Warehouse Id */ + warehouse_id: number; + /** Warehouse Name */ + warehouse_name: string; + /** Quantity */ + quantity: string; + /** Locked Quantity */ + locked_quantity: string; + /** Available Quantity */ + available_quantity: string; + }; + /** InventoryUpdate */ + InventoryUpdate: { + /** Quantity */ + quantity?: number | string | null; + /** Locked Quantity */ + locked_quantity?: number | string | null; + /** Batch Number */ + batch_number?: string | null; + /** Location */ + location?: string | null; + }; + /** LoginRequest */ + LoginRequest: { + /** Username */ + username: string; + /** Password */ + password: string; + }; + /** MaterialConsumptionItem */ + MaterialConsumptionItem: { + /** Material Id */ + material_id: number; + /** Quantity */ + quantity: number; + /** Remark */ + remark?: string | null; + }; + /** MaterialConsumptionRequest */ + MaterialConsumptionRequest: { + /** Items */ + items: components["schemas"]["MaterialConsumptionItem"][]; + }; + /** + * MaterialPriceHistoryCreate + * @description 创建物料价格历史的请求模型 + */ + MaterialPriceHistoryCreate: { + /** + * Price + * @description 物料价格 + */ + price: number; + /** + * Supplier Id + * @description 供应商ID + */ + supplier_id?: number | null; + /** + * Remark + * @description 备注 + */ + remark?: string | null; + }; + /** + * MaterialPriceHistoryResponse + * @description 物料价格历史的响应模型 + */ + MaterialPriceHistoryResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku: string; + /** Product Name */ + product_name: string; + /** Price */ + price: number; + /** + * Effective Date + * Format: date-time + */ + effective_date: string; + /** Supplier Id */ + supplier_id: number | null; + /** Supplier Name */ + supplier_name: string | null; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** + * MaterialPriceTrendResponse + * @description 物料价格趋势的响应模型 + */ + MaterialPriceTrendResponse: { + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku: string; + /** Product Name */ + product_name: string; + /** Current Price */ + current_price: number; + /** Price Change */ + price_change: number; + /** Price Change Percent */ + price_change_percent: number; + /** Price History */ + price_history: components["schemas"]["PriceHistoryItem"][]; + }; + /** + * MaterialSupplierCreate + * @description 创建物料供应商关联的请求模型 + */ + MaterialSupplierCreate: { + /** + * Supplier Id + * @description 供应商ID + */ + supplier_id: number; + /** + * Is Primary + * @description 是否为主要供应商 + * @default false + */ + is_primary: boolean; + /** + * Contact Person + * @description 联系人 + */ + contact_person?: string | null; + /** + * Contact Phone + * @description 联系电话 + */ + contact_phone?: string | null; + /** + * Lead Time + * @description 交货周期(天) + */ + lead_time?: number | null; + /** + * Min Order Quantity + * @description 最小订购量 + */ + min_order_quantity?: number | null; + }; + /** + * MaterialSupplierResponse + * @description 物料供应商关联的响应模型 + */ + MaterialSupplierResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku: string; + /** Product Name */ + product_name: string; + /** Supplier Id */ + supplier_id: number; + /** Supplier Name */ + supplier_name: string; + /** Is Primary */ + is_primary: boolean; + /** Contact Person */ + contact_person: string | null; + /** Contact Phone */ + contact_phone: string | null; + /** Lead Time */ + lead_time: number | null; + /** Min Order Quantity */ + min_order_quantity: number | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** PaginatedResponse[FinanceTransactionResponse] */ + PaginatedResponse_FinanceTransactionResponse_: { + /** Items */ + items: components["schemas"]["FinanceTransactionResponse"][]; + /** Total */ + total: number; + /** Skip */ + skip: number; + /** Limit */ + limit: number; + }; + /** PaginatedResponse[InventoryResponse] */ + PaginatedResponse_InventoryResponse_: { + /** Items */ + items: components["schemas"]["InventoryResponse"][]; + /** Total */ + total: number; + /** Skip */ + skip: number; + /** Limit */ + limit: number; + }; + /** PaginatedResponse[PurchaseOrderResponse] */ + PaginatedResponse_PurchaseOrderResponse_: { + /** Items */ + items: components["schemas"]["PurchaseOrderResponse"][]; + /** Total */ + total: number; + /** Skip */ + skip: number; + /** Limit */ + limit: number; + }; + /** PaginatedResponse[SalesOrderResponse] */ + PaginatedResponse_SalesOrderResponse_: { + /** Items */ + items: components["schemas"]["SalesOrderResponse"][]; + /** Total */ + total: number; + /** Skip */ + skip: number; + /** Limit */ + limit: number; + }; + /** PaginatedResponse[StockMovementResponse] */ + PaginatedResponse_StockMovementResponse_: { + /** Items */ + items: components["schemas"]["StockMovementResponse"][]; + /** Total */ + total: number; + /** Skip */ + skip: number; + /** Limit */ + limit: number; + }; + /** PartnerProductStatementItemResponse */ + PartnerProductStatementItemResponse: { + /** Partner Id */ + partner_id: number; + /** Partner Name */ + partner_name: string; + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku?: string | null; + /** Product Name */ + product_name: string; + /** Order Count */ + order_count: number; + /** Order Quantity */ + order_quantity: string; + /** Order Amount */ + order_amount: string; + /** Settled Amount */ + settled_amount: string; + /** Outstanding Amount */ + outstanding_amount: string; + /** Period Year */ + period_year: number; + /** Period Quarter */ + period_quarter?: number | null; + }; + /** PartnerStatementItemResponse */ + PartnerStatementItemResponse: { + /** Partner Id */ + partner_id: number; + /** Partner Name */ + partner_name: string; + /** Order Count */ + order_count: number; + /** Transaction Count */ + transaction_count: number; + /** Order Total */ + order_total: string; + /** Settled Total */ + settled_total: string; + /** Transaction Total */ + transaction_total: string; + /** Outstanding Total */ + outstanding_total: string; + /** Period Year */ + period_year: number; + /** Period Quarter */ + period_quarter?: number | null; + }; + /** PayableItemResponse */ + PayableItemResponse: { + /** Order Id */ + order_id: number; + /** Order No */ + order_no: string; + /** Supplier Id */ + supplier_id: number; + /** Supplier Name */ + supplier_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Total Amount */ + total_amount: string; + /** Paid Amount */ + paid_amount: string; + /** Payable Amount */ + payable_amount: string; + /** Status */ + status: string; + }; + /** PaymentCreate */ + PaymentCreate: { + /** Amount */ + amount: number | string; + /** Txn Date */ + txn_date?: string | null; + /** + * Method + * @default bank + */ + method: string; + /** Account Name */ + account_name?: string | null; + /** Remark */ + remark?: string | null; + /** Allocations */ + allocations: components["schemas"]["FinanceAllocationCreate"][]; + /** Supplier Id */ + supplier_id: number; + }; + /** PermissionCreate */ + PermissionCreate: { + /** Code */ + code: string; + /** Name */ + name: string; + /** Module */ + module?: string | null; + /** Description */ + description?: string | null; + }; + /** PermissionResponse */ + PermissionResponse: { + /** Id */ + id: number; + /** Code */ + code: string; + /** Name */ + name: string; + /** Module */ + module: string | null; + /** Description */ + description: string | null; + }; + /** + * PriceHistoryItem + * @description 价格历史项 + */ + PriceHistoryItem: { + /** + * Date + * Format: date-time + */ + date: string; + /** Price */ + price: number; + /** Supplier Name */ + supplier_name: string | null; + }; + /** ProductBOMResponse */ + ProductBOMResponse: { + /** Product Id */ + product_id: number; + /** Product Name */ + product_name: string; + /** Total Material Cost */ + total_material_cost: string; + /** Items */ + items: components["schemas"]["ProductMaterialItemResponse"][]; + }; + /** ProductBOMUpdate */ + ProductBOMUpdate: { + /** Items */ + items: components["schemas"]["ProductMaterialItemUpdate"][]; + }; + /** ProductCreate */ + ProductCreate: { + /** Sku */ + sku: string; + /** Name */ + name: string; + /** Description */ + description?: string | null; + /** Category */ + category?: string | null; + /** + * Unit + * @default 件 + */ + unit: string; + /** + * Item Type + * @default finished + */ + item_type: string; + /** + * Cost Price + * @default 0 + */ + cost_price: number | string; + /** + * Sale Price + * @default 0 + */ + sale_price: number | string; + /** + * Min Stock + * @default 0 + */ + min_stock: number; + /** + * Max Stock + * @default 1000 + */ + max_stock: number; + }; + /** ProductMaterialItemResponse */ + ProductMaterialItemResponse: { + /** Material Id */ + material_id: number; + /** Material Sku */ + material_sku: string; + /** Material Name */ + material_name: string; + /** Quantity */ + quantity: string; + /** Unit Cost */ + unit_cost: string; + /** Line Cost */ + line_cost: string; + }; + /** ProductMaterialItemUpdate */ + ProductMaterialItemUpdate: { + /** Material Id */ + material_id: number; + /** Quantity */ + quantity: number | string; + /** + * Loss Rate + * @default 0 + */ + loss_rate: number | string; + }; + /** ProductResponse */ + ProductResponse: { + /** Id */ + id: number; + /** Sku */ + sku: string; + /** Name */ + name: string; + /** Description */ + description: string | null; + /** Category */ + category: string | null; + /** Unit */ + unit: string; + /** Item Type */ + item_type: string; + /** Cost Price */ + cost_price: string; + /** Sale Price */ + sale_price: string; + /** Min Stock */ + min_stock: number; + /** Max Stock */ + max_stock: number; + /** + * Material Cost + * @default 0 + */ + material_cost: string; + /** Is Active */ + is_active: boolean; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** ProductionMaterialPlanItemResponse */ + ProductionMaterialPlanItemResponse: { + /** Material Id */ + material_id: number; + /** Material Sku */ + material_sku: string; + /** Material Name */ + material_name: string; + /** Required Quantity */ + required_quantity: string; + /** Available Quantity */ + available_quantity: string; + /** Shortage Quantity */ + shortage_quantity: string; + /** Unit Cost */ + unit_cost: string; + /** Required Cost */ + required_cost: string; + }; + /** PurchaseOrderCreate */ + PurchaseOrderCreate: { + /** Supplier Id */ + supplier_id: number; + /** Expected Date */ + expected_date?: string | null; + /** Remark */ + remark?: string | null; + /** Items */ + items: components["schemas"]["PurchaseOrderItemCreate"][]; + }; + /** PurchaseOrderDetailResponse */ + PurchaseOrderDetailResponse: { + /** Id */ + id: number; + /** Order No */ + order_no: string; + /** Supplier Name */ + supplier_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Expected Date */ + expected_date: string | null; + /** Status */ + status: string; + /** Receipt Status */ + receipt_status: string; + /** Payment Status */ + payment_status: string; + /** Total Amount */ + total_amount: string; + /** Paid Amount */ + paid_amount: string; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** Received Date */ + received_date: string | null; + /** Paid Date */ + paid_date: string | null; + /** Supplier Id */ + supplier_id: number; + /** Items */ + items: components["schemas"]["PurchaseOrderItemResponse"][]; + }; + /** PurchaseOrderItemCreate */ + PurchaseOrderItemCreate: { + /** Product Id */ + product_id: number; + /** + * Quantity + * @description 采购数量 + */ + quantity: number; + /** + * Unit Price + * @description 单价(可选,不填则使用物料成本价格) + */ + unit_price?: number | string | null; + /** Remark */ + remark?: string | null; + }; + /** PurchaseOrderItemResponse */ + PurchaseOrderItemResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku: string; + /** Product Name */ + product_name: string; + /** Quantity */ + quantity: number; + /** Received Quantity */ + received_quantity: number; + /** Unit Price */ + unit_price: string; + /** Amount */ + amount: string; + /** Remark */ + remark?: string | null; + }; + /** PurchaseOrderReceiveItem */ + PurchaseOrderReceiveItem: { + /** Item Id */ + item_id: number; + /** Receive Quantity */ + receive_quantity: number; + }; + /** PurchaseOrderReceiveRequest */ + PurchaseOrderReceiveRequest: { + /** Warehouse Id */ + warehouse_id?: number | null; + /** Items */ + items: components["schemas"]["PurchaseOrderReceiveItem"][]; + /** Remark */ + remark?: string | null; + }; + /** PurchaseOrderResponse */ + PurchaseOrderResponse: { + /** Id */ + id: number; + /** Order No */ + order_no: string; + /** Supplier Name */ + supplier_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Expected Date */ + expected_date: string | null; + /** Status */ + status: string; + /** Receipt Status */ + receipt_status: string; + /** Payment Status */ + payment_status: string; + /** Total Amount */ + total_amount: string; + /** Paid Amount */ + paid_amount: string; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** Received Date */ + received_date: string | null; + /** Paid Date */ + paid_date: string | null; + }; + /** ReceiptCreate */ + ReceiptCreate: { + /** Amount */ + amount: number | string; + /** Txn Date */ + txn_date?: string | null; + /** + * Method + * @default bank + */ + method: string; + /** Account Name */ + account_name?: string | null; + /** Remark */ + remark?: string | null; + /** Allocations */ + allocations: components["schemas"]["FinanceAllocationCreate"][]; + /** Customer Id */ + customer_id: number; + }; + /** ReceivableItemResponse */ + ReceivableItemResponse: { + /** Order Id */ + order_id: number; + /** Order No */ + order_no: string; + /** Customer Id */ + customer_id: number; + /** Customer Name */ + customer_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Total Amount */ + total_amount: string; + /** Received Amount */ + received_amount: string; + /** Receivable Amount */ + receivable_amount: string; + /** Status */ + status: string; + }; + /** RoleCreate */ + RoleCreate: { + /** Code */ + code: string; + /** Name */ + name: string; + /** Description */ + description?: string | null; + }; + /** RoleResponse */ + RoleResponse: { + /** Id */ + id: number; + /** Code */ + code: string; + /** Name */ + name: string; + /** Description */ + description: string | null; + /** Is System */ + is_system: boolean; + /** Permissions */ + permissions: string[]; + }; + /** SalesOrderCreate */ + SalesOrderCreate: { + /** Customer Id */ + customer_id: number; + /** Delivery Date */ + delivery_date?: string | null; + /** Remark */ + remark?: string | null; + /** Items */ + items: components["schemas"]["SalesOrderItemCreate"][]; + }; + /** SalesOrderDetailResponse */ + SalesOrderDetailResponse: { + /** Id */ + id: number; + /** Order No */ + order_no: string; + /** Customer Name */ + customer_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Delivery Date */ + delivery_date: string | null; + /** Manufacturing Date */ + manufacturing_date: string | null; + /** Actual Delivery Date */ + actual_delivery_date: string | null; + /** Actual Payment Date */ + actual_payment_date: string | null; + /** Status */ + status: string; + /** Delivery Status */ + delivery_status: string; + /** Payment Status */ + payment_status: string; + /** Production Status */ + production_status: string; + /** Production No */ + production_no: string | null; + /** Planned Material Cost */ + planned_material_cost: string; + /** Actual Material Cost */ + actual_material_cost: string; + /** Total Amount */ + total_amount: string; + /** Received Amount */ + received_amount: string; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** Customer Id */ + customer_id: number; + /** Items */ + items: components["schemas"]["SalesOrderItemResponse"][]; + }; + /** SalesOrderIssueRequest */ + SalesOrderIssueRequest: { + /** Warehouse Id */ + warehouse_id: number; + /** Production No */ + production_no?: string | null; + /** Remark */ + remark?: string | null; + }; + /** SalesOrderIssueResponse */ + SalesOrderIssueResponse: { + /** Sales Order Id */ + sales_order_id: number; + /** Order No */ + order_no: string; + /** Production No */ + production_no: string; + /** Movement Count */ + movement_count: number; + /** Planned Material Cost */ + planned_material_cost: string; + /** Actual Material Cost */ + actual_material_cost: string; + /** Cost Deviation */ + cost_deviation: string; + /** Cost Deviation Rate */ + cost_deviation_rate: string; + /** Production Status */ + production_status: string; + }; + /** SalesOrderItemCreate */ + SalesOrderItemCreate: { + /** Product Id */ + product_id?: number | null; + /** Product Sku */ + product_sku?: string | null; + /** Product Name */ + product_name?: string | null; + /** Product Category */ + product_category?: string | null; + /** + * Product Unit + * @default 件 + */ + product_unit: string | null; + /** Quantity */ + quantity: number; + /** Unit Price */ + unit_price: number | string; + /** Remark */ + remark?: string | null; + }; + /** SalesOrderItemResponse */ + SalesOrderItemResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Quantity */ + quantity: number; + /** Delivered Quantity */ + delivered_quantity: number; + /** Unit Price */ + unit_price: string; + /** Amount */ + amount: string; + /** Remark */ + remark?: string | null; + }; + /** SalesOrderProductionPlanResponse */ + SalesOrderProductionPlanResponse: { + /** Sales Order Id */ + sales_order_id: number; + /** Order No */ + order_no: string; + /** Customer Name */ + customer_name: string; + /** Production No */ + production_no: string; + /** Planned Material Cost */ + planned_material_cost: string; + /** Items */ + items: components["schemas"]["ProductionMaterialPlanItemResponse"][]; + }; + /** SalesOrderResponse */ + SalesOrderResponse: { + /** Id */ + id: number; + /** Order No */ + order_no: string; + /** Customer Name */ + customer_name: string; + /** + * Order Date + * Format: date-time + */ + order_date: string; + /** Delivery Date */ + delivery_date: string | null; + /** Manufacturing Date */ + manufacturing_date: string | null; + /** Actual Delivery Date */ + actual_delivery_date: string | null; + /** Actual Payment Date */ + actual_payment_date: string | null; + /** Status */ + status: string; + /** Delivery Status */ + delivery_status: string; + /** Payment Status */ + payment_status: string; + /** Production Status */ + production_status: string; + /** Production No */ + production_no: string | null; + /** Planned Material Cost */ + planned_material_cost: string; + /** Actual Material Cost */ + actual_material_cost: string; + /** Total Amount */ + total_amount: string; + /** Received Amount */ + received_amount: string; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** SalesOrderStatusUpdate */ + SalesOrderStatusUpdate: { + /** Status */ + status: string; + }; + /** StockMovementCreate */ + StockMovementCreate: { + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku?: string | null; + /** Warehouse Id */ + warehouse_id: number; + /** Movement Type */ + movement_type: string; + /** Quantity */ + quantity: number | string; + /** Unit Price */ + unit_price?: number | string | null; + /** Remark */ + remark?: string | null; + }; + /** StockMovementResponse */ + StockMovementResponse: { + /** Id */ + id: number; + /** Product Id */ + product_id: number; + /** Product Sku */ + product_sku: string | null; + /** Product Name */ + product_name: string; + /** Movement Type */ + movement_type: string; + /** Quantity */ + quantity: string; + /** Before Quantity */ + before_quantity: string; + /** After Quantity */ + after_quantity: string; + /** Reference No */ + reference_no: string | null; + /** Remark */ + remark: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** SupplierCreate */ + SupplierCreate: { + /** Code */ + code?: string | null; + /** Name */ + name: string; + /** Contact Person */ + contact_person?: string | null; + /** Phone */ + phone?: string | null; + /** Email */ + email?: string | null; + /** Address */ + address?: string | null; + }; + /** SupplierResponse */ + SupplierResponse: { + /** Id */ + id: number; + /** Code */ + code: string | null; + /** Name */ + name: string; + /** Contact Person */ + contact_person: string | null; + /** Phone */ + phone: string | null; + /** Email */ + email: string | null; + /** Is Active */ + is_active: boolean; + }; + /** Token */ + Token: { + /** Access Token */ + access_token: string; + /** Token Type */ + token_type: string; + user: components["schemas"]["UserResponse"]; + }; + /** UserCreate */ + UserCreate: { + /** Username */ + username: string; + /** Email */ + email: string; + /** Password */ + password: string; + /** Full Name */ + full_name?: string | null; + /** + * Role Ids + * @default [] + */ + role_ids: number[]; + }; + /** UserResponse */ + UserResponse: { + /** Id */ + id: number; + /** Username */ + username: string; + /** Email */ + email: string; + /** Full Name */ + full_name: string | null; + /** Is Active */ + is_active: boolean; + /** + * Is Superuser + * @default false + */ + is_superuser: boolean; + /** Roles */ + roles: string[]; + }; + /** UserUpdate */ + UserUpdate: { + /** Email */ + email?: string | null; + /** Full Name */ + full_name?: string | null; + /** Is Active */ + is_active?: boolean | null; + /** Role Ids */ + role_ids?: number[] | null; + }; + /** ValidationError */ + ValidationError: { + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; + /** Input */ + input?: unknown; + /** Context */ + ctx?: Record; + }; + /** WarehouseCreate */ + WarehouseCreate: { + /** Code */ + code?: string | null; + /** Name */ + name: string; + /** Address */ + address?: string | null; + /** Manager */ + manager?: string | null; + /** Phone */ + phone?: string | null; + }; + /** WarehouseResponse */ + WarehouseResponse: { + /** Id */ + id: number; + /** Code */ + code: string | null; + /** Name */ + name: string; + /** Address */ + address: string | null; + /** Manager */ + manager: string | null; + /** Is Active */ + is_active: boolean; + /** Is Default */ + is_default: boolean; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + login_api_auth_login_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/x-www-form-urlencoded": components["schemas"]["Body_login_api_auth_login_post"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Token"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + login_json_api_auth_login_json_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["LoginRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Token"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_current_user_info_api_auth_me_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserResponse"]; + }; + }; + }; + }; + logout_api_auth_logout_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + list_users_api_auth_users_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserResponse"][]; + }; + }; + }; + }; + create_user_api_auth_users_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_user_api_auth_users__user_id__put: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UserUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UserResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_user_api_auth_users__user_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + reset_user_password_api_auth_users__user_id__reset_password_put: { + parameters: { + query: { + new_password: string; + }; + header?: never; + path: { + user_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_roles_api_auth_roles_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RoleResponse"][]; + }; + }; + }; + }; + create_role_api_auth_roles_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RoleCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RoleResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_role_api_auth_roles__role_id__put: { + parameters: { + query?: never; + header?: never; + path: { + role_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RoleCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RoleResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_role_api_auth_roles__role_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + role_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + set_role_permissions_api_auth_roles__role_id__permissions_put: { + parameters: { + query?: never; + header?: never; + path: { + role_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": number[]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_permissions_api_auth_permissions_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PermissionResponse"][]; + }; + }; + }; + }; + create_permission_api_auth_permissions_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PermissionCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PermissionResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_permission_api_auth_permissions__permission_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + permission_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_products_api_products_get: { + parameters: { + query?: { + skip?: number; + limit?: number; + search?: string | null; + category?: string | null; + item_type?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProductResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_product_api_products_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProductCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProductResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_product_api_products__product_id__put: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProductCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProductResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_product_api_products__product_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_product_bom_api_products__product_id__materials_get: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProductBOMResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + replace_product_bom_api_products__product_id__materials_put: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ProductBOMUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ProductBOMResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_suppliers_api_suppliers_get: { + parameters: { + query?: { + skip?: number; + limit?: number; + search?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupplierResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_supplier_api_suppliers_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SupplierCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupplierResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_supplier_api_suppliers__supplier_id__put: { + parameters: { + query?: never; + header?: never; + path: { + supplier_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SupplierCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SupplierResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_supplier_api_suppliers__supplier_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + supplier_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_customers_api_customers_get: { + parameters: { + query?: { + skip?: number; + limit?: number; + search?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CustomerResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_customer_api_customers_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CustomerCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CustomerResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_customer_api_customers__customer_id__put: { + parameters: { + query?: never; + header?: never; + path: { + customer_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CustomerCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CustomerResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_customer_api_customers__customer_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + customer_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_warehouses_api_warehouses_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["WarehouseResponse"][]; + }; + }; + }; + }; + create_warehouse_api_warehouses_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["WarehouseCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["WarehouseResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_inventory_api_inventory_get: { + parameters: { + query?: { + warehouse_id?: number | null; + product_id?: number | null; + low_stock?: boolean; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponse_InventoryResponse_"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_inventory_api_inventory_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["InventoryCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InventoryResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_inventory_api_inventory__inventory_id__put: { + parameters: { + query?: never; + header?: never; + path: { + inventory_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["InventoryUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InventoryResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_inventory_api_inventory__inventory_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + inventory_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_stock_movements_api_stock_movements_get: { + parameters: { + query?: { + product_id?: number | null; + movement_type?: string | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponse_StockMovementResponse_"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_stock_movement_api_stock_movements_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["StockMovementCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["StockMovementResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_purchase_orders_api_purchase_orders_get: { + parameters: { + query?: { + status?: string | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponse_PurchaseOrderResponse_"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_purchase_order_api_purchase_orders_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PurchaseOrderCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PurchaseOrderResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_purchase_order_detail_api_purchase_orders__order_id__get: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PurchaseOrderDetailResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_purchase_order_api_purchase_orders__order_id__put: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PurchaseOrderCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PurchaseOrderResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_purchase_order_api_purchase_orders__order_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_purchase_order_status_api_purchase_orders__order_id__status_patch: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + receive_purchase_order_api_purchase_orders__order_id__receive_post: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PurchaseOrderReceiveRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PurchaseOrderDetailResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_sales_orders_api_sales_orders_get: { + parameters: { + query?: { + status?: string | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponse_SalesOrderResponse_"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_sales_order_api_sales_orders_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SalesOrderCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_sales_order_detail_api_sales_orders__order_id__get: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderDetailResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_sales_order_api_sales_orders__order_id__put: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SalesOrderCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + delete_sales_order_api_sales_orders__order_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + update_sales_order_status_api_sales_orders__order_id__status_patch: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SalesOrderStatusUpdate"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + consume_materials_api_sales_orders__order_id__consume_materials_post: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MaterialConsumptionRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_sales_order_production_plan_api_sales_orders__order_id__production_plan_get: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderProductionPlanResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + issue_sales_order_materials_api_sales_orders__order_id__issue_materials_post: { + parameters: { + query?: never; + header?: never; + path: { + order_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SalesOrderIssueRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SalesOrderIssueResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_material_price_history_api_materials__product_id__price_history_get: { + parameters: { + query?: { + limit?: number; + }; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialPriceHistoryResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_material_price_history_api_materials__product_id__price_history_post: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MaterialPriceHistoryCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialPriceHistoryResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_material_price_trend_api_materials__product_id__price_trend_get: { + parameters: { + query?: { + months?: number; + }; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialPriceTrendResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_material_suppliers_api_materials__product_id__suppliers_get: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialSupplierResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + add_material_supplier_api_materials__product_id__suppliers_post: { + parameters: { + query?: never; + header?: never; + path: { + product_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MaterialSupplierCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialSupplierResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + remove_material_supplier_api_materials_suppliers__supplier_id__delete: { + parameters: { + query?: never; + header?: never; + path: { + supplier_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_supplier_materials_api_materials_suppliers__supplier_id__materials_get: { + parameters: { + query?: never; + header?: never; + path: { + supplier_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MaterialSupplierResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_dashboard_api_dashboard_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + create_receipt_api_finance_receipts_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ReceiptCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FinanceTransactionResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + create_payment_api_finance_payments_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PaymentCreate"]; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FinanceTransactionResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_transactions_api_finance_transactions_get: { + parameters: { + query?: { + txn_type?: string | null; + status?: string | null; + year?: number | null; + quarter?: number | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PaginatedResponse_FinanceTransactionResponse_"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + void_transaction_api_finance_transactions__transaction_id__void_post: { + parameters: { + query?: never; + header?: never; + path: { + transaction_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_receivables_api_finance_receivables_get: { + parameters: { + query?: { + year?: number | null; + quarter?: number | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ReceivableItemResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + list_payables_api_finance_payables_get: { + parameters: { + query?: { + year?: number | null; + quarter?: number | null; + skip?: number; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PayableItemResponse"][]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_finance_summary_api_finance_summary_get: { + parameters: { + query?: { + year?: number | null; + quarter?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FinanceSummaryResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_partner_statement_api_finance_partner_statement__partner_type__get: { + parameters: { + query?: { + year?: number | null; + quarter?: number | null; + }; + header?: never; + path: { + partner_type: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FinancePartnerStatementResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + get_partner_product_statement_api_finance_partner_product_statement__partner_type__get: { + parameters: { + query?: { + partner_id?: number | null; + year?: number | null; + quarter?: number | null; + }; + header?: never; + path: { + partner_type: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FinancePartnerProductStatementResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + health_health_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + }; + }; + spa_fallback__full_path__get: { + parameters: { + query?: never; + header?: never; + path: { + full_path: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; +} diff --git a/frontend/src/types/schemas.ts b/frontend/src/types/schemas.ts new file mode 100644 index 0000000..f1f895e --- /dev/null +++ b/frontend/src/types/schemas.ts @@ -0,0 +1,11 @@ +/** + * 后端 OpenAPI schema 类型快捷引用 + * + * 类型由 `npm run gen:api` 从后端 openapi-inventory.json 自动生成(见 ./api.ts)。 + * 后端字段变更后重新生成即可,前端类型自动同步,杜绝手写类型漂移。 + * + * 用法:Schema<'FinanceSummaryResponse'>、Schema<'SalesOrderResponse'>[] + */ +import type { components } from './api' + +export type Schema = components['schemas'][K] diff --git a/openapi-inventory.json b/openapi-inventory.json new file mode 100644 index 0000000..c58b73d --- /dev/null +++ b/openapi-inventory.json @@ -0,0 +1,8169 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Gemold - 进销存管理系统", + "version": "4.0.0" + }, + "paths": { + "/api/auth/login": { + "post": { + "tags": [ + "认证" + ], + "summary": "Login", + "operationId": "login_api_auth_login_post", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Body_login_api_auth_login_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Token" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/login/json": { + "post": { + "tags": [ + "认证" + ], + "summary": "Login Json", + "operationId": "login_json_api_auth_login_json_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Token" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/me": { + "get": { + "tags": [ + "认证" + ], + "summary": "Get Current User Info", + "operationId": "get_current_user_info_api_auth_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/logout": { + "post": { + "tags": [ + "认证" + ], + "summary": "Logout", + "operationId": "logout_api_auth_logout_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/auth/users": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Users", + "operationId": "list_users_api_auth_users_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserResponse" + }, + "type": "array", + "title": "Response List Users Api Auth Users Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create User", + "operationId": "create_user_api_auth_users_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/users/{user_id}": { + "put": { + "tags": [ + "认证" + ], + "summary": "Update User", + "operationId": "update_user_api_auth_users__user_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete User", + "operationId": "delete_user_api_auth_users__user_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/users/{user_id}/reset-password": { + "put": { + "tags": [ + "认证" + ], + "summary": "Reset User Password", + "operationId": "reset_user_password_api_auth_users__user_id__reset_password_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + }, + { + "name": "new_password", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "New Password" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/roles": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Roles", + "operationId": "list_roles_api_auth_roles_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/RoleResponse" + }, + "type": "array", + "title": "Response List Roles Api Auth Roles Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create Role", + "operationId": "create_role_api_auth_roles_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/roles/{role_id}": { + "put": { + "tags": [ + "认证" + ], + "summary": "Update Role", + "operationId": "update_role_api_auth_roles__role_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete Role", + "operationId": "delete_role_api_auth_roles__role_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/roles/{role_id}/permissions": { + "put": { + "tags": [ + "认证" + ], + "summary": "Set Role Permissions", + "operationId": "set_role_permissions_api_auth_roles__role_id__permissions_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "integer" + }, + "title": "Permission Ids" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/permissions": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Permissions", + "operationId": "list_permissions_api_auth_permissions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PermissionResponse" + }, + "type": "array", + "title": "Response List Permissions Api Auth Permissions Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create Permission", + "operationId": "create_permission_api_auth_permissions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/permissions/{permission_id}": { + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete Permission", + "operationId": "delete_permission_api_auth_permissions__permission_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "permission_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Permission Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products": { + "get": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "List Products", + "operationId": "list_products_api_products_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + }, + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + } + }, + { + "name": "item_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Item Type" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductResponse" + }, + "title": "Response List Products Api Products Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Create Product", + "operationId": "create_product_api_products_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products/{product_id}": { + "put": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Update Product", + "operationId": "update_product_api_products__product_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Delete Product", + "operationId": "delete_product_api_products__product_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products/{product_id}/materials": { + "get": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Get Product Bom", + "operationId": "get_product_bom_api_products__product_id__materials_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Replace Product Bom", + "operationId": "replace_product_bom_api_products__product_id__materials_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/suppliers": { + "get": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "List Suppliers", + "operationId": "list_suppliers_api_suppliers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SupplierResponse" + }, + "title": "Response List Suppliers Api Suppliers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Create Supplier", + "operationId": "create_supplier_api_suppliers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/suppliers/{supplier_id}": { + "put": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Update Supplier", + "operationId": "update_supplier_api_suppliers__supplier_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Delete Supplier", + "operationId": "delete_supplier_api_suppliers__supplier_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/customers": { + "get": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "List Customers", + "operationId": "list_customers_api_customers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerResponse" + }, + "title": "Response List Customers Api Customers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Create Customer", + "operationId": "create_customer_api_customers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/customers/{customer_id}": { + "put": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Update Customer", + "operationId": "update_customer_api_customers__customer_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "customer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Customer Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Delete Customer", + "operationId": "delete_customer_api_customers__customer_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "customer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Customer Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/warehouses": { + "get": { + "tags": [ + "进销存", + "仓库管理" + ], + "summary": "List Warehouses", + "operationId": "list_warehouses_api_warehouses_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/WarehouseResponse" + }, + "type": "array", + "title": "Response List Warehouses Api Warehouses Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "进销存", + "仓库管理" + ], + "summary": "Create Warehouse", + "operationId": "create_warehouse_api_warehouses_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarehouseCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarehouseResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/inventory": { + "get": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "List Inventory", + "operationId": "list_inventory_api_inventory_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "warehouse_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Warehouse Id" + } + }, + { + "name": "product_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + } + }, + { + "name": "low_stock", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Low Stock" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_InventoryResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Create Inventory", + "operationId": "create_inventory_api_inventory_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/inventory/{inventory_id}": { + "put": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Update Inventory", + "operationId": "update_inventory_api_inventory__inventory_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "inventory_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Inventory Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Delete Inventory", + "operationId": "delete_inventory_api_inventory__inventory_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "inventory_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Inventory Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/stock-movements": { + "post": { + "tags": [ + "进销存", + "库存变动" + ], + "summary": "Create Stock Movement", + "operationId": "create_stock_movement_api_stock_movements_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockMovementCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockMovementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "库存变动" + ], + "summary": "List Stock Movements", + "operationId": "list_stock_movements_api_stock_movements_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + } + }, + { + "name": "movement_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Movement Type" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_StockMovementResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders": { + "get": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "List Purchase Orders", + "operationId": "list_purchase_orders_api_purchase_orders_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Status" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_PurchaseOrderResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Create Purchase Order", + "operationId": "create_purchase_order_api_purchase_orders_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}": { + "get": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Get Purchase Order Detail", + "operationId": "get_purchase_order_detail_api_purchase_orders__order_id__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Update Purchase Order", + "operationId": "update_purchase_order_api_purchase_orders__order_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Delete Purchase Order", + "operationId": "delete_purchase_order_api_purchase_orders__order_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}/status": { + "patch": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Update Purchase Order Status", + "operationId": "update_purchase_order_status_api_purchase_orders__order_id__status_patch", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Status" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}/receive": { + "post": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Receive Purchase Order", + "operationId": "receive_purchase_order_api_purchase_orders__order_id__receive_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderReceiveRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "List Sales Orders", + "operationId": "list_sales_orders_api_sales_orders_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Status" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_SalesOrderResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Create Sales Order", + "operationId": "create_sales_order_api_sales_orders_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Get Sales Order Detail", + "operationId": "get_sales_order_detail_api_sales_orders__order_id__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Update Sales Order", + "operationId": "update_sales_order_api_sales_orders__order_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Delete Sales Order", + "operationId": "delete_sales_order_api_sales_orders__order_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/status": { + "patch": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Update Sales Order Status", + "operationId": "update_sales_order_status_api_sales_orders__order_id__status_patch", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderStatusUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/consume-materials": { + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Consume Materials", + "description": "记录销售订单的物料消耗(在已发料基础上记录额外消耗,如报废/超耗)", + "operationId": "consume_materials_api_sales_orders__order_id__consume_materials_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialConsumptionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/production-plan": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Get Sales Order Production Plan", + "operationId": "get_sales_order_production_plan_api_sales_orders__order_id__production_plan_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderProductionPlanResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/issue-materials": { + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Issue Sales Order Materials", + "operationId": "issue_sales_order_materials_api_sales_orders__order_id__issue_materials_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderIssueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderIssueResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/price-history": { + "post": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Add Material Price History", + "operationId": "add_material_price_history_api_materials__product_id__price_history_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceHistoryCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceHistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Price History", + "operationId": "get_material_price_history_api_materials__product_id__price_history_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialPriceHistoryResponse" + }, + "title": "Response Get Material Price History Api Materials Product Id Price History Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/price-trend": { + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Price Trend", + "operationId": "get_material_price_trend_api_materials__product_id__price_trend_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + }, + { + "name": "months", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 24, + "minimum": 1, + "default": 6, + "title": "Months" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceTrendResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/suppliers": { + "post": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Add Material Supplier", + "operationId": "add_material_supplier_api_materials__product_id__suppliers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialSupplierCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Suppliers", + "operationId": "get_material_suppliers_api_materials__product_id__suppliers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + }, + "title": "Response Get Material Suppliers Api Materials Product Id Suppliers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/suppliers/{supplier_id}": { + "delete": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Remove Material Supplier", + "operationId": "remove_material_supplier_api_materials_suppliers__supplier_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/suppliers/{supplier_id}/materials": { + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Supplier Materials", + "operationId": "get_supplier_materials_api_materials_suppliers__supplier_id__materials_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + }, + "title": "Response Get Supplier Materials Api Materials Suppliers Supplier Id Materials Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/dashboard": { + "get": { + "tags": [ + "进销存", + "仪表盘" + ], + "summary": "Get Dashboard", + "operationId": "get_dashboard_api_dashboard_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/receipts": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Create Receipt", + "operationId": "create_receipt_api_finance_receipts_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReceiptCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/payments": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Create Payment", + "operationId": "create_payment_api_finance_payments_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/transactions": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Transactions", + "operationId": "list_transactions_api_finance_transactions_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "txn_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Txn Type" + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "confirmed", + "title": "Status" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_FinanceTransactionResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/transactions/{transaction_id}/void": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Void Transaction", + "operationId": "void_transaction_api_finance_transactions__transaction_id__void_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Transaction Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/receivables": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Receivables", + "operationId": "list_receivables_api_finance_receivables_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReceivableItemResponse" + }, + "title": "Response List Receivables Api Finance Receivables Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/payables": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Payables", + "operationId": "list_payables_api_finance_payables_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PayableItemResponse" + }, + "title": "Response List Payables Api Finance Payables Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/summary": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Finance Summary", + "operationId": "get_finance_summary_api_finance_summary_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceSummaryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/partner-statement/{partner_type}": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Partner Statement", + "operationId": "get_partner_statement_api_finance_partner_statement__partner_type__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "partner_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Partner Type" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinancePartnerStatementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/partner-product-statement/{partner_type}": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Partner Product Statement", + "operationId": "get_partner_product_statement_api_finance_partner_product_statement__partner_type__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "partner_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Partner Type" + } + }, + { + "name": "partner_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Partner Id" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinancePartnerProductStatementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/health": { + "get": { + "summary": "Health", + "operationId": "health_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/{full_path}": { + "get": { + "summary": "Spa Fallback", + "operationId": "spa_fallback__full_path__get", + "parameters": [ + { + "name": "full_path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Full Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Body_login_api_auth_login_post": { + "properties": { + "grant_type": { + "anyOf": [ + { + "type": "string", + "pattern": "^password$" + }, + { + "type": "null" + } + ], + "title": "Grant Type" + }, + "username": { + "type": "string", + "title": "Username" + }, + "password": { + "type": "string", + "format": "password", + "title": "Password" + }, + "scope": { + "type": "string", + "title": "Scope", + "default": "" + }, + "client_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Client Id" + }, + "client_secret": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "format": "password", + "title": "Client Secret" + } + }, + "type": "object", + "required": [ + "username", + "password" + ], + "title": "Body_login_api_auth_login_post" + }, + "CustomerCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "CustomerCreate" + }, + "CustomerResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "contact_person", + "phone", + "email", + "is_active" + ], + "title": "CustomerResponse" + }, + "FinanceAllocationCreate": { + "properties": { + "order_type": { + "type": "string", + "enum": [ + "sales", + "purchase" + ], + "title": "Order Type" + }, + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "allocated_amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Allocated Amount" + } + }, + "type": "object", + "required": [ + "order_type", + "order_id", + "allocated_amount" + ], + "title": "FinanceAllocationCreate" + }, + "FinanceAllocationResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_type": { + "type": "string", + "title": "Order Type" + }, + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "allocated_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Allocated Amount" + } + }, + "type": "object", + "required": [ + "id", + "order_type", + "order_id", + "allocated_amount" + ], + "title": "FinanceAllocationResponse" + }, + "FinancePartnerProductStatementResponse": { + "properties": { + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "year": { + "type": "integer", + "title": "Year" + }, + "quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "partner_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Partner Id" + }, + "order_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Amount Total" + }, + "settled_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Amount Total" + }, + "outstanding_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Amount Total" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PartnerProductStatementItemResponse" + }, + "type": "array", + "title": "Items", + "default": [] + } + }, + "type": "object", + "required": [ + "partner_type", + "year", + "period_label", + "order_amount_total", + "settled_amount_total", + "outstanding_amount_total" + ], + "title": "FinancePartnerProductStatementResponse" + }, + "FinancePartnerStatementResponse": { + "properties": { + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "year": { + "type": "integer", + "title": "Year" + }, + "quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "order_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Total" + }, + "settled_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Total" + }, + "transaction_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Transaction Total" + }, + "outstanding_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Total" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PartnerStatementItemResponse" + }, + "type": "array", + "title": "Items", + "default": [] + } + }, + "type": "object", + "required": [ + "partner_type", + "year", + "period_label", + "order_total", + "settled_total", + "transaction_total", + "outstanding_total" + ], + "title": "FinancePartnerStatementResponse" + }, + "FinanceSummaryResponse": { + "properties": { + "receivable_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Receivable Total" + }, + "payable_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Payable Total" + }, + "monthly_receipt_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Monthly Receipt Total" + }, + "monthly_payment_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Monthly Payment Total" + }, + "selected_year": { + "type": "integer", + "title": "Selected Year" + }, + "selected_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Selected Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "period_receipt_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Period Receipt Total", + "default": "0" + }, + "period_payment_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Period Payment Total", + "default": "0" + }, + "overdue_receivable_count": { + "type": "integer", + "title": "Overdue Receivable Count", + "default": 0 + }, + "overdue_payable_count": { + "type": "integer", + "title": "Overdue Payable Count", + "default": 0 + } + }, + "type": "object", + "required": [ + "receivable_total", + "payable_total", + "monthly_receipt_total", + "monthly_payment_total", + "selected_year", + "period_label" + ], + "title": "FinanceSummaryResponse" + }, + "FinanceTransactionResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "txn_no": { + "type": "string", + "title": "Txn No" + }, + "txn_type": { + "type": "string", + "enum": [ + "receipt", + "payment" + ], + "title": "Txn Type" + }, + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Partner Name" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "txn_date": { + "type": "string", + "format": "date-time", + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "status": { + "type": "string", + "enum": [ + "confirmed", + "voided" + ], + "title": "Status" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationResponse" + }, + "type": "array", + "title": "Allocations", + "default": [] + } + }, + "type": "object", + "required": [ + "id", + "txn_no", + "txn_type", + "partner_type", + "partner_id", + "amount", + "txn_date", + "method", + "account_name", + "status", + "remark", + "created_at" + ], + "title": "FinanceTransactionResponse" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "InventoryCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity", + "default": "0" + }, + "locked_quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Locked Quantity", + "default": "0" + }, + "batch_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Batch Number" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Location" + } + }, + "type": "object", + "required": [ + "product_id", + "warehouse_id" + ], + "title": "InventoryCreate" + }, + "InventoryResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "warehouse_name": { + "type": "string", + "title": "Warehouse Name" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "locked_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Locked Quantity" + }, + "available_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Available Quantity" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_name", + "product_sku", + "warehouse_id", + "warehouse_name", + "quantity", + "locked_quantity", + "available_quantity" + ], + "title": "InventoryResponse" + }, + "InventoryUpdate": { + "properties": { + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Quantity" + }, + "locked_quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Locked Quantity" + }, + "batch_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Batch Number" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Location" + } + }, + "type": "object", + "title": "InventoryUpdate" + }, + "LoginRequest": { + "properties": { + "username": { + "type": "string", + "title": "Username" + }, + "password": { + "type": "string", + "title": "Password" + } + }, + "type": "object", + "required": [ + "username", + "password" + ], + "title": "LoginRequest" + }, + "MaterialConsumptionItem": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "quantity": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Quantity" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "material_id", + "quantity" + ], + "title": "MaterialConsumptionItem" + }, + "MaterialConsumptionRequest": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/MaterialConsumptionItem" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "MaterialConsumptionRequest" + }, + "MaterialPriceHistoryCreate": { + "properties": { + "price": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Price", + "description": "物料价格" + }, + "supplier_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Supplier Id", + "description": "供应商ID" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark", + "description": "备注" + } + }, + "type": "object", + "required": [ + "price" + ], + "title": "MaterialPriceHistoryCreate", + "description": "创建物料价格历史的请求模型" + }, + "MaterialPriceHistoryResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "price": { + "type": "number", + "title": "Price" + }, + "effective_date": { + "type": "string", + "format": "date-time", + "title": "Effective Date" + }, + "supplier_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Supplier Id" + }, + "supplier_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Supplier Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "price", + "effective_date", + "supplier_id", + "supplier_name", + "remark", + "created_at" + ], + "title": "MaterialPriceHistoryResponse", + "description": "物料价格历史的响应模型" + }, + "MaterialPriceTrendResponse": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "current_price": { + "type": "number", + "title": "Current Price" + }, + "price_change": { + "type": "number", + "title": "Price Change" + }, + "price_change_percent": { + "type": "number", + "title": "Price Change Percent" + }, + "price_history": { + "items": { + "$ref": "#/components/schemas/PriceHistoryItem" + }, + "type": "array", + "title": "Price History" + } + }, + "type": "object", + "required": [ + "product_id", + "product_sku", + "product_name", + "current_price", + "price_change", + "price_change_percent", + "price_history" + ], + "title": "MaterialPriceTrendResponse", + "description": "物料价格趋势的响应模型" + }, + "MaterialSupplierCreate": { + "properties": { + "supplier_id": { + "type": "integer", + "title": "Supplier Id", + "description": "供应商ID" + }, + "is_primary": { + "type": "boolean", + "title": "Is Primary", + "description": "是否为主要供应商", + "default": false + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person", + "description": "联系人" + }, + "contact_phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Phone", + "description": "联系电话" + }, + "lead_time": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Lead Time", + "description": "交货周期(天)" + }, + "min_order_quantity": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Min Order Quantity", + "description": "最小订购量" + } + }, + "type": "object", + "required": [ + "supplier_id" + ], + "title": "MaterialSupplierCreate", + "description": "创建物料供应商关联的请求模型" + }, + "MaterialSupplierResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "is_primary": { + "type": "boolean", + "title": "Is Primary" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "contact_phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Phone" + }, + "lead_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Lead Time" + }, + "min_order_quantity": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Min Order Quantity" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "supplier_id", + "supplier_name", + "is_primary", + "contact_person", + "contact_phone", + "lead_time", + "min_order_quantity", + "created_at", + "updated_at" + ], + "title": "MaterialSupplierResponse", + "description": "物料供应商关联的响应模型" + }, + "PaginatedResponse_FinanceTransactionResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[FinanceTransactionResponse]" + }, + "PaginatedResponse_InventoryResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/InventoryResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[InventoryResponse]" + }, + "PaginatedResponse_PurchaseOrderResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[PurchaseOrderResponse]" + }, + "PaginatedResponse_SalesOrderResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[SalesOrderResponse]" + }, + "PaginatedResponse_StockMovementResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/StockMovementResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[StockMovementResponse]" + }, + "PartnerProductStatementItemResponse": { + "properties": { + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "type": "string", + "title": "Partner Name" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "order_count": { + "type": "integer", + "title": "Order Count" + }, + "order_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Quantity" + }, + "order_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Amount" + }, + "settled_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Amount" + }, + "outstanding_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Amount" + }, + "period_year": { + "type": "integer", + "title": "Period Year" + }, + "period_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Period Quarter" + } + }, + "type": "object", + "required": [ + "partner_id", + "partner_name", + "product_id", + "product_name", + "order_count", + "order_quantity", + "order_amount", + "settled_amount", + "outstanding_amount", + "period_year" + ], + "title": "PartnerProductStatementItemResponse" + }, + "PartnerStatementItemResponse": { + "properties": { + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "type": "string", + "title": "Partner Name" + }, + "order_count": { + "type": "integer", + "title": "Order Count" + }, + "transaction_count": { + "type": "integer", + "title": "Transaction Count" + }, + "order_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Total" + }, + "settled_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Total" + }, + "transaction_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Transaction Total" + }, + "outstanding_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Total" + }, + "period_year": { + "type": "integer", + "title": "Period Year" + }, + "period_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Period Quarter" + } + }, + "type": "object", + "required": [ + "partner_id", + "partner_name", + "order_count", + "transaction_count", + "order_total", + "settled_total", + "transaction_total", + "outstanding_total", + "period_year" + ], + "title": "PartnerStatementItemResponse" + }, + "PayableItemResponse": { + "properties": { + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "payable_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Payable Amount" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "order_id", + "order_no", + "supplier_id", + "supplier_name", + "order_date", + "total_amount", + "paid_amount", + "payable_amount", + "status" + ], + "title": "PayableItemResponse" + }, + "PaymentCreate": { + "properties": { + "amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Amount" + }, + "txn_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method", + "default": "bank" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationCreate" + }, + "type": "array", + "title": "Allocations" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + } + }, + "type": "object", + "required": [ + "amount", + "allocations", + "supplier_id" + ], + "title": "PaymentCreate" + }, + "PermissionCreate": { + "properties": { + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Module" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "code", + "name" + ], + "title": "PermissionCreate" + }, + "PermissionResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Module" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "module", + "description" + ], + "title": "PermissionResponse" + }, + "PriceHistoryItem": { + "properties": { + "date": { + "type": "string", + "format": "date-time", + "title": "Date" + }, + "price": { + "type": "number", + "title": "Price" + }, + "supplier_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Supplier Name" + } + }, + "type": "object", + "required": [ + "date", + "price", + "supplier_name" + ], + "title": "PriceHistoryItem", + "description": "价格历史项" + }, + "ProductBOMResponse": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "total_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Material Cost" + }, + "items": { + "items": { + "$ref": "#/components/schemas/ProductMaterialItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "product_id", + "product_name", + "total_material_cost", + "items" + ], + "title": "ProductBOMResponse" + }, + "ProductBOMUpdate": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/ProductMaterialItemUpdate" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "ProductBOMUpdate" + }, + "ProductCreate": { + "properties": { + "sku": { + "type": "string", + "title": "Sku" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + }, + "unit": { + "type": "string", + "title": "Unit", + "default": "件" + }, + "item_type": { + "type": "string", + "title": "Item Type", + "default": "finished" + }, + "cost_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Cost Price", + "default": "0" + }, + "sale_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Sale Price", + "default": "0" + }, + "min_stock": { + "type": "integer", + "title": "Min Stock", + "default": 0 + }, + "max_stock": { + "type": "integer", + "title": "Max Stock", + "default": 1000 + } + }, + "type": "object", + "required": [ + "sku", + "name" + ], + "title": "ProductCreate" + }, + "ProductMaterialItemResponse": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "material_sku": { + "type": "string", + "title": "Material Sku" + }, + "material_name": { + "type": "string", + "title": "Material Name" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "unit_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Cost" + }, + "line_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Line Cost" + } + }, + "type": "object", + "required": [ + "material_id", + "material_sku", + "material_name", + "quantity", + "unit_cost", + "line_cost" + ], + "title": "ProductMaterialItemResponse" + }, + "ProductMaterialItemUpdate": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity" + }, + "loss_rate": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Loss Rate", + "default": "0" + } + }, + "type": "object", + "required": [ + "material_id", + "quantity" + ], + "title": "ProductMaterialItemUpdate" + }, + "ProductResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "sku": { + "type": "string", + "title": "Sku" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + }, + "unit": { + "type": "string", + "title": "Unit" + }, + "item_type": { + "type": "string", + "title": "Item Type" + }, + "cost_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Price" + }, + "sale_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Sale Price" + }, + "min_stock": { + "type": "integer", + "title": "Min Stock" + }, + "max_stock": { + "type": "integer", + "title": "Max Stock" + }, + "material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Material Cost", + "default": "0" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "sku", + "name", + "description", + "category", + "unit", + "item_type", + "cost_price", + "sale_price", + "min_stock", + "max_stock", + "is_active", + "created_at" + ], + "title": "ProductResponse" + }, + "ProductionMaterialPlanItemResponse": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "material_sku": { + "type": "string", + "title": "Material Sku" + }, + "material_name": { + "type": "string", + "title": "Material Name" + }, + "required_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Required Quantity" + }, + "available_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Available Quantity" + }, + "shortage_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Shortage Quantity" + }, + "unit_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Cost" + }, + "required_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Required Cost" + } + }, + "type": "object", + "required": [ + "material_id", + "material_sku", + "material_name", + "required_quantity", + "available_quantity", + "shortage_quantity", + "unit_cost", + "required_cost" + ], + "title": "ProductionMaterialPlanItemResponse" + }, + "PurchaseOrderCreate": { + "properties": { + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderItemCreate" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "supplier_id", + "items" + ], + "title": "PurchaseOrderCreate" + }, + "PurchaseOrderDetailResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "receipt_status": { + "type": "string", + "title": "Receipt Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "received_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Received Date" + }, + "paid_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Paid Date" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "supplier_name", + "order_date", + "expected_date", + "status", + "receipt_status", + "payment_status", + "total_amount", + "paid_amount", + "remark", + "created_at", + "received_date", + "paid_date", + "supplier_id", + "items" + ], + "title": "PurchaseOrderDetailResponse" + }, + "PurchaseOrderItemCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Quantity", + "description": "采购数量" + }, + "unit_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Unit Price", + "description": "单价(可选,不填则使用物料成本价格)" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "product_id", + "quantity" + ], + "title": "PurchaseOrderItemCreate" + }, + "PurchaseOrderItemResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "quantity": { + "type": "integer", + "title": "Quantity" + }, + "received_quantity": { + "type": "integer", + "title": "Received Quantity" + }, + "unit_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Price" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "quantity", + "received_quantity", + "unit_price", + "amount" + ], + "title": "PurchaseOrderItemResponse" + }, + "PurchaseOrderReceiveItem": { + "properties": { + "item_id": { + "type": "integer", + "title": "Item Id" + }, + "receive_quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Receive Quantity" + } + }, + "type": "object", + "required": [ + "item_id", + "receive_quantity" + ], + "title": "PurchaseOrderReceiveItem" + }, + "PurchaseOrderReceiveRequest": { + "properties": { + "warehouse_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Warehouse Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderReceiveItem" + }, + "type": "array", + "minItems": 1, + "title": "Items" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "PurchaseOrderReceiveRequest" + }, + "PurchaseOrderResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "receipt_status": { + "type": "string", + "title": "Receipt Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "received_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Received Date" + }, + "paid_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Paid Date" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "supplier_name", + "order_date", + "expected_date", + "status", + "receipt_status", + "payment_status", + "total_amount", + "paid_amount", + "remark", + "created_at", + "received_date", + "paid_date" + ], + "title": "PurchaseOrderResponse" + }, + "ReceiptCreate": { + "properties": { + "amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Amount" + }, + "txn_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method", + "default": "bank" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationCreate" + }, + "type": "array", + "title": "Allocations" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + } + }, + "type": "object", + "required": [ + "amount", + "allocations", + "customer_id" + ], + "title": "ReceiptCreate" + }, + "ReceivableItemResponse": { + "properties": { + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "receivable_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Receivable Amount" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "order_id", + "order_no", + "customer_id", + "customer_name", + "order_date", + "total_amount", + "received_amount", + "receivable_amount", + "status" + ], + "title": "ReceivableItemResponse" + }, + "RoleCreate": { + "properties": { + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "code", + "name" + ], + "title": "RoleCreate" + }, + "RoleResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "is_system": { + "type": "boolean", + "title": "Is System" + }, + "permissions": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Permissions" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "description", + "is_system", + "permissions" + ], + "title": "RoleResponse" + }, + "SalesOrderCreate": { + "properties": { + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderItemCreate" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "customer_id", + "items" + ], + "title": "SalesOrderCreate" + }, + "SalesOrderDetailResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "manufacturing_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Manufacturing Date" + }, + "actual_delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Delivery Date" + }, + "actual_payment_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Payment Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "delivery_status": { + "type": "string", + "title": "Delivery Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "production_status": { + "type": "string", + "title": "Production Status" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "customer_name", + "order_date", + "delivery_date", + "manufacturing_date", + "actual_delivery_date", + "actual_payment_date", + "status", + "delivery_status", + "payment_status", + "production_status", + "production_no", + "planned_material_cost", + "actual_material_cost", + "total_amount", + "received_amount", + "remark", + "created_at", + "customer_id", + "items" + ], + "title": "SalesOrderDetailResponse" + }, + "SalesOrderIssueRequest": { + "properties": { + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "warehouse_id" + ], + "title": "SalesOrderIssueRequest" + }, + "SalesOrderIssueResponse": { + "properties": { + "sales_order_id": { + "type": "integer", + "title": "Sales Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "production_no": { + "type": "string", + "title": "Production No" + }, + "movement_count": { + "type": "integer", + "title": "Movement Count" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "cost_deviation": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Deviation" + }, + "cost_deviation_rate": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Deviation Rate" + }, + "production_status": { + "type": "string", + "title": "Production Status" + } + }, + "type": "object", + "required": [ + "sales_order_id", + "order_no", + "production_no", + "movement_count", + "planned_material_cost", + "actual_material_cost", + "cost_deviation", + "cost_deviation_rate", + "production_status" + ], + "title": "SalesOrderIssueResponse" + }, + "SalesOrderItemCreate": { + "properties": { + "product_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Name" + }, + "product_category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Category" + }, + "product_unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Unit", + "default": "件" + }, + "quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Quantity" + }, + "unit_price": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Unit Price" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "quantity", + "unit_price" + ], + "title": "SalesOrderItemCreate" + }, + "SalesOrderItemResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "quantity": { + "type": "integer", + "title": "Quantity" + }, + "delivered_quantity": { + "type": "integer", + "title": "Delivered Quantity" + }, + "unit_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Price" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "quantity", + "delivered_quantity", + "unit_price", + "amount" + ], + "title": "SalesOrderItemResponse" + }, + "SalesOrderProductionPlanResponse": { + "properties": { + "sales_order_id": { + "type": "integer", + "title": "Sales Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "production_no": { + "type": "string", + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "items": { + "items": { + "$ref": "#/components/schemas/ProductionMaterialPlanItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "sales_order_id", + "order_no", + "customer_name", + "production_no", + "planned_material_cost", + "items" + ], + "title": "SalesOrderProductionPlanResponse" + }, + "SalesOrderResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "manufacturing_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Manufacturing Date" + }, + "actual_delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Delivery Date" + }, + "actual_payment_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Payment Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "delivery_status": { + "type": "string", + "title": "Delivery Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "production_status": { + "type": "string", + "title": "Production Status" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "customer_name", + "order_date", + "delivery_date", + "manufacturing_date", + "actual_delivery_date", + "actual_payment_date", + "status", + "delivery_status", + "payment_status", + "production_status", + "production_no", + "planned_material_cost", + "actual_material_cost", + "total_amount", + "received_amount", + "remark", + "created_at" + ], + "title": "SalesOrderResponse" + }, + "SalesOrderStatusUpdate": { + "properties": { + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "status" + ], + "title": "SalesOrderStatusUpdate" + }, + "StockMovementCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "movement_type": { + "type": "string", + "title": "Movement Type" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity" + }, + "unit_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Unit Price" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "product_id", + "warehouse_id", + "movement_type", + "quantity" + ], + "title": "StockMovementCreate" + }, + "StockMovementResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "movement_type": { + "type": "string", + "title": "Movement Type" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "before_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Before Quantity" + }, + "after_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "After Quantity" + }, + "reference_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Reference No" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "movement_type", + "quantity", + "before_quantity", + "after_quantity", + "reference_no", + "remark", + "created_at" + ], + "title": "StockMovementResponse" + }, + "SupplierCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "SupplierCreate" + }, + "SupplierResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "contact_person", + "phone", + "email", + "is_active" + ], + "title": "SupplierResponse" + }, + "Token": { + "properties": { + "access_token": { + "type": "string", + "title": "Access Token" + }, + "token_type": { + "type": "string", + "title": "Token Type" + }, + "user": { + "$ref": "#/components/schemas/UserResponse" + } + }, + "type": "object", + "required": [ + "access_token", + "token_type", + "user" + ], + "title": "Token" + }, + "UserCreate": { + "properties": { + "username": { + "type": "string", + "title": "Username" + }, + "email": { + "type": "string", + "title": "Email" + }, + "password": { + "type": "string", + "title": "Password" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "role_ids": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Role Ids", + "default": [] + } + }, + "type": "object", + "required": [ + "username", + "email", + "password" + ], + "title": "UserCreate" + }, + "UserResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "username": { + "type": "string", + "title": "Username" + }, + "email": { + "type": "string", + "title": "Email" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "is_superuser": { + "type": "boolean", + "title": "Is Superuser", + "default": false + }, + "roles": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Roles" + } + }, + "type": "object", + "required": [ + "id", + "username", + "email", + "full_name", + "is_active", + "roles" + ], + "title": "UserResponse" + }, + "UserUpdate": { + "properties": { + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "is_active": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Active" + }, + "role_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Role Ids" + } + }, + "type": "object", + "title": "UserUpdate" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + }, + "input": { + "title": "Input" + }, + "ctx": { + "type": "object", + "title": "Context" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "WarehouseCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + }, + "manager": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Manager" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "WarehouseCreate" + }, + "WarehouseResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + }, + "manager": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Manager" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "is_default": { + "type": "boolean", + "title": "Is Default" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "address", + "manager", + "is_active", + "is_default" + ], + "title": "WarehouseResponse" + } + }, + "securitySchemes": { + "OAuth2PasswordBearer": { + "type": "oauth2", + "flows": { + "password": { + "scopes": {}, + "tokenUrl": "/api/auth/login" + } + } + } + } + } +} \ No newline at end of file diff --git a/openapi.json b/openapi.json new file mode 100644 index 0000000..96b2e6a --- /dev/null +++ b/openapi.json @@ -0,0 +1,8777 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Gemold - 模具制造管理系统", + "description": "模具制造行业综合管理平台,包含模具分析、进销存管理等功能", + "version": "4.0.0" + }, + "paths": { + "/api/auth/login": { + "post": { + "tags": [ + "认证" + ], + "summary": "Login", + "operationId": "login_api_auth_login_post", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Body_login_api_auth_login_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Token" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/login/json": { + "post": { + "tags": [ + "认证" + ], + "summary": "Login Json", + "operationId": "login_json_api_auth_login_json_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Token" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/me": { + "get": { + "tags": [ + "认证" + ], + "summary": "Get Current User Info", + "operationId": "get_current_user_info_api_auth_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/logout": { + "post": { + "tags": [ + "认证" + ], + "summary": "Logout", + "operationId": "logout_api_auth_logout_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/auth/users": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Users", + "operationId": "list_users_api_auth_users_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/UserResponse" + }, + "type": "array", + "title": "Response List Users Api Auth Users Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create User", + "operationId": "create_user_api_auth_users_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/users/{user_id}": { + "put": { + "tags": [ + "认证" + ], + "summary": "Update User", + "operationId": "update_user_api_auth_users__user_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete User", + "operationId": "delete_user_api_auth_users__user_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/users/{user_id}/reset-password": { + "put": { + "tags": [ + "认证" + ], + "summary": "Reset User Password", + "operationId": "reset_user_password_api_auth_users__user_id__reset_password_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "User Id" + } + }, + { + "name": "new_password", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "New Password" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/roles": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Roles", + "operationId": "list_roles_api_auth_roles_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/RoleResponse" + }, + "type": "array", + "title": "Response List Roles Api Auth Roles Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create Role", + "operationId": "create_role_api_auth_roles_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/roles/{role_id}": { + "put": { + "tags": [ + "认证" + ], + "summary": "Update Role", + "operationId": "update_role_api_auth_roles__role_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete Role", + "operationId": "delete_role_api_auth_roles__role_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/roles/{role_id}/permissions": { + "put": { + "tags": [ + "认证" + ], + "summary": "Set Role Permissions", + "operationId": "set_role_permissions_api_auth_roles__role_id__permissions_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "role_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Role Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "integer" + }, + "title": "Permission Ids" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/auth/permissions": { + "get": { + "tags": [ + "认证" + ], + "summary": "List Permissions", + "operationId": "list_permissions_api_auth_permissions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PermissionResponse" + }, + "type": "array", + "title": "Response List Permissions Api Auth Permissions Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "认证" + ], + "summary": "Create Permission", + "operationId": "create_permission_api_auth_permissions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/auth/permissions/{permission_id}": { + "delete": { + "tags": [ + "认证" + ], + "summary": "Delete Permission", + "operationId": "delete_permission_api_auth_permissions__permission_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "permission_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Permission Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products": { + "get": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "List Products", + "operationId": "list_products_api_products_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + }, + { + "name": "category", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + } + }, + { + "name": "item_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Item Type" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductResponse" + }, + "title": "Response List Products Api Products Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Create Product", + "operationId": "create_product_api_products_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products/{product_id}": { + "put": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Update Product", + "operationId": "update_product_api_products__product_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Delete Product", + "operationId": "delete_product_api_products__product_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/products/{product_id}/materials": { + "get": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Get Product Bom", + "operationId": "get_product_bom_api_products__product_id__materials_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "产品管理" + ], + "summary": "Replace Product Bom", + "operationId": "replace_product_bom_api_products__product_id__materials_put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductBOMResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/suppliers": { + "get": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "List Suppliers", + "operationId": "list_suppliers_api_suppliers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SupplierResponse" + }, + "title": "Response List Suppliers Api Suppliers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Create Supplier", + "operationId": "create_supplier_api_suppliers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/suppliers/{supplier_id}": { + "put": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Update Supplier", + "operationId": "update_supplier_api_suppliers__supplier_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "供应商管理" + ], + "summary": "Delete Supplier", + "operationId": "delete_supplier_api_suppliers__supplier_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/customers": { + "get": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "List Customers", + "operationId": "list_customers_api_customers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerResponse" + }, + "title": "Response List Customers Api Customers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Create Customer", + "operationId": "create_customer_api_customers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/customers/{customer_id}": { + "put": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Update Customer", + "operationId": "update_customer_api_customers__customer_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "customer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Customer Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "客户管理" + ], + "summary": "Delete Customer", + "operationId": "delete_customer_api_customers__customer_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "customer_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Customer Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/warehouses": { + "get": { + "tags": [ + "进销存", + "仓库管理" + ], + "summary": "List Warehouses", + "operationId": "list_warehouses_api_warehouses_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/WarehouseResponse" + }, + "type": "array", + "title": "Response List Warehouses Api Warehouses Get" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + }, + "post": { + "tags": [ + "进销存", + "仓库管理" + ], + "summary": "Create Warehouse", + "operationId": "create_warehouse_api_warehouses_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarehouseCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarehouseResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/inventory": { + "get": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "List Inventory", + "operationId": "list_inventory_api_inventory_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "warehouse_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Warehouse Id" + } + }, + { + "name": "product_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + } + }, + { + "name": "low_stock", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Low Stock" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_InventoryResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Create Inventory", + "operationId": "create_inventory_api_inventory_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/inventory/{inventory_id}": { + "put": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Update Inventory", + "operationId": "update_inventory_api_inventory__inventory_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "inventory_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Inventory Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "库存管理" + ], + "summary": "Delete Inventory", + "operationId": "delete_inventory_api_inventory__inventory_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "inventory_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Inventory Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/stock-movements": { + "post": { + "tags": [ + "进销存", + "库存变动" + ], + "summary": "Create Stock Movement", + "operationId": "create_stock_movement_api_stock_movements_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockMovementCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StockMovementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "库存变动" + ], + "summary": "List Stock Movements", + "operationId": "list_stock_movements_api_stock_movements_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + } + }, + { + "name": "movement_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Movement Type" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_StockMovementResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders": { + "get": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "List Purchase Orders", + "operationId": "list_purchase_orders_api_purchase_orders_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Status" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_PurchaseOrderResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Create Purchase Order", + "operationId": "create_purchase_order_api_purchase_orders_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}": { + "get": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Get Purchase Order Detail", + "operationId": "get_purchase_order_detail_api_purchase_orders__order_id__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Update Purchase Order", + "operationId": "update_purchase_order_api_purchase_orders__order_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Delete Purchase Order", + "operationId": "delete_purchase_order_api_purchase_orders__order_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}/status": { + "patch": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Update Purchase Order Status", + "operationId": "update_purchase_order_status_api_purchase_orders__order_id__status_patch", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Status" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/purchase-orders/{order_id}/receive": { + "post": { + "tags": [ + "进销存", + "采购订单" + ], + "summary": "Receive Purchase Order", + "operationId": "receive_purchase_order_api_purchase_orders__order_id__receive_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderReceiveRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PurchaseOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "List Sales Orders", + "operationId": "list_sales_orders_api_sales_orders_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Status" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_SalesOrderResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Create Sales Order", + "operationId": "create_sales_order_api_sales_orders_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Get Sales Order Detail", + "operationId": "get_sales_order_detail_api_sales_orders__order_id__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderDetailResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Update Sales Order", + "operationId": "update_sales_order_api_sales_orders__order_id__put", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Delete Sales Order", + "operationId": "delete_sales_order_api_sales_orders__order_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/status": { + "patch": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Update Sales Order Status", + "operationId": "update_sales_order_status_api_sales_orders__order_id__status_patch", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderStatusUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/consume-materials": { + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Consume Materials", + "description": "记录销售订单的物料消耗(在已发料基础上记录额外消耗,如报废/超耗)", + "operationId": "consume_materials_api_sales_orders__order_id__consume_materials_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialConsumptionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/production-plan": { + "get": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Get Sales Order Production Plan", + "operationId": "get_sales_order_production_plan_api_sales_orders__order_id__production_plan_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderProductionPlanResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/sales-orders/{order_id}/issue-materials": { + "post": { + "tags": [ + "进销存", + "销售订单" + ], + "summary": "Issue Sales Order Materials", + "operationId": "issue_sales_order_materials_api_sales_orders__order_id__issue_materials_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "order_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Order Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderIssueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SalesOrderIssueResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/price-history": { + "post": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Add Material Price History", + "operationId": "add_material_price_history_api_materials__product_id__price_history_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceHistoryCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceHistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Price History", + "operationId": "get_material_price_history_api_materials__product_id__price_history_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialPriceHistoryResponse" + }, + "title": "Response Get Material Price History Api Materials Product Id Price History Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/price-trend": { + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Price Trend", + "operationId": "get_material_price_trend_api_materials__product_id__price_trend_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + }, + { + "name": "months", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 24, + "minimum": 1, + "default": 6, + "title": "Months" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialPriceTrendResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/{product_id}/suppliers": { + "post": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Add Material Supplier", + "operationId": "add_material_supplier_api_materials__product_id__suppliers_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialSupplierCreate" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Material Suppliers", + "operationId": "get_material_suppliers_api_materials__product_id__suppliers_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "product_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Product Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + }, + "title": "Response Get Material Suppliers Api Materials Product Id Suppliers Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/suppliers/{supplier_id}": { + "delete": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Remove Material Supplier", + "operationId": "remove_material_supplier_api_materials_suppliers__supplier_id__delete", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/materials/suppliers/{supplier_id}/materials": { + "get": { + "tags": [ + "进销存", + "物料管理" + ], + "summary": "Get Supplier Materials", + "operationId": "get_supplier_materials_api_materials_suppliers__supplier_id__materials_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "supplier_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Supplier Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialSupplierResponse" + }, + "title": "Response Get Supplier Materials Api Materials Suppliers Supplier Id Materials Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/dashboard": { + "get": { + "tags": [ + "进销存", + "仪表盘" + ], + "summary": "Get Dashboard", + "operationId": "get_dashboard_api_dashboard_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/receipts": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Create Receipt", + "operationId": "create_receipt_api_finance_receipts_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReceiptCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/payments": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Create Payment", + "operationId": "create_payment_api_finance_payments_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/finance/transactions": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Transactions", + "operationId": "list_transactions_api_finance_transactions_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "txn_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Txn Type" + } + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": "confirmed", + "title": "Status" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "default": 20, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedResponse_FinanceTransactionResponse_" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/transactions/{transaction_id}/void": { + "post": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Void Transaction", + "operationId": "void_transaction_api_finance_transactions__transaction_id__void_post", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "transaction_id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Transaction Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/receivables": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Receivables", + "operationId": "list_receivables_api_finance_receivables_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReceivableItemResponse" + }, + "title": "Response List Receivables Api Finance Receivables Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/payables": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "List Payables", + "operationId": "list_payables_api_finance_payables_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 200, + "minimum": 1, + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PayableItemResponse" + }, + "title": "Response List Payables Api Finance Payables Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/summary": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Finance Summary", + "operationId": "get_finance_summary_api_finance_summary_get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinanceSummaryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/partner-statement/{partner_type}": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Partner Statement", + "operationId": "get_partner_statement_api_finance_partner_statement__partner_type__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "partner_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Partner Type" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinancePartnerStatementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/finance/partner-product-statement/{partner_type}": { + "get": { + "tags": [ + "进销存", + "财务管理" + ], + "summary": "Get Partner Product Statement", + "operationId": "get_partner_product_statement_api_finance_partner_product_statement__partner_type__get", + "security": [ + { + "OAuth2PasswordBearer": [] + } + ], + "parameters": [ + { + "name": "partner_type", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Partner Type" + } + }, + { + "name": "partner_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Partner Id" + } + }, + { + "name": "year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2100, + "minimum": 2000 + }, + { + "type": "null" + } + ], + "title": "Year" + } + }, + { + "name": "quarter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 4, + "minimum": 1 + }, + { + "type": "null" + } + ], + "title": "Quarter" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FinancePartnerProductStatementResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/aluminum-price/current": { + "get": { + "tags": [ + "铝金属价格" + ], + "summary": "Aluminum Current Price", + "operationId": "aluminum_current_price_api_aluminum_price_current_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/aluminum-price/history": { + "get": { + "tags": [ + "铝金属价格" + ], + "summary": "Aluminum Price History", + "operationId": "aluminum_price_history_api_aluminum_price_history_get", + "parameters": [ + { + "name": "days", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 365, + "minimum": 7, + "default": 30, + "title": "Days" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/health": { + "get": { + "summary": "Health", + "operationId": "health_api_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "summary": "Health", + "operationId": "health_api_health_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/upload": { + "post": { + "summary": "Upload Stp", + "description": "上传STP文件并存储到数据库", + "operationId": "upload_stp_api_upload_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_stp_api_upload_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/api/status/{task_id}": { + "post": { + "summary": "Get Status", + "description": "获取任务状态\n\n优先返回内存中的任务信息;\n如果内存中不存在,则从 PostgreSQL + RustFS 组装一个持久化的任务视图,\n结构与内存任务保持尽量一致,便于前端集中展示总结性信息。", + "operationId": "get_status_api_status__task_id__post", + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "summary": "Get Status", + "description": "获取任务状态\n\n优先返回内存中的任务信息;\n如果内存中不存在,则从 PostgreSQL + RustFS 组装一个持久化的任务视图,\n结构与内存任务保持尽量一致,便于前端集中展示总结性信息。", + "operationId": "get_status_api_status__task_id__get", + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/result/{task_id}": { + "post": { + "summary": "Result Page", + "description": "结果详情页面", + "operationId": "result_page_api_result__task_id__post", + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "summary": "Result Page", + "description": "结果详情页面", + "operationId": "result_page_api_result__task_id__get", + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/history": { + "get": { + "summary": "Get File History", + "description": "获取按文件名分组的文件历史记录(支持多上传)", + "operationId": "get_file_history_api_history_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "summary": "Get File History", + "description": "获取按文件名分组的文件历史记录(支持多上传)", + "operationId": "get_file_history_api_history_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/history/{filename}": { + "post": { + "summary": "Get File Records", + "description": "获取指定文件名的所有上传记录(支持多上传历史)", + "operationId": "get_file_records_api_history__filename__post", + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Filename" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "summary": "Get File Records", + "description": "获取指定文件名的所有上传记录(支持多上传历史)", + "operationId": "get_file_records_api_history__filename__get", + "parameters": [ + { + "name": "filename", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Filename" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/debug/tasks": { + "get": { + "summary": "Debug Tasks", + "description": "调试接口:查看所有任务", + "operationId": "debug_tasks_api_debug_tasks_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "summary": "Debug Tasks", + "description": "调试接口:查看所有任务", + "operationId": "debug_tasks_api_debug_tasks_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/cam/plan": { + "post": { + "summary": "Generate Cam Plan", + "description": "基于任务分模结果生成 CAM 准备包(MVP)。", + "operationId": "generate_cam_plan_api_cam_plan_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "OAuth2PasswordBearer": [] + } + ] + } + }, + "/health": { + "get": { + "summary": "Health", + "operationId": "health_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "post": { + "summary": "Health", + "operationId": "health_health_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Root", + "operationId": "root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/moldinsight": { + "get": { + "summary": "Moldinsight", + "operationId": "moldinsight_moldinsight_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/inventory": { + "get": { + "summary": "Inventory", + "operationId": "inventory_inventory_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/login": { + "get": { + "summary": "Login", + "operationId": "login_login_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/users": { + "get": { + "summary": "Users", + "operationId": "users_users_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/moldinsight/result/{task_id}": { + "get": { + "summary": "Moldinsight Result", + "operationId": "moldinsight_result_moldinsight_result__task_id__get", + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/_design-system": { + "get": { + "summary": "Design System", + "operationId": "design_system__design_system_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/_release": { + "get": { + "summary": "Release", + "operationId": "release__release_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Body_login_api_auth_login_post": { + "properties": { + "grant_type": { + "anyOf": [ + { + "type": "string", + "pattern": "^password$" + }, + { + "type": "null" + } + ], + "title": "Grant Type" + }, + "username": { + "type": "string", + "title": "Username" + }, + "password": { + "type": "string", + "format": "password", + "title": "Password" + }, + "scope": { + "type": "string", + "title": "Scope", + "default": "" + }, + "client_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Client Id" + }, + "client_secret": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "format": "password", + "title": "Client Secret" + } + }, + "type": "object", + "required": [ + "username", + "password" + ], + "title": "Body_login_api_auth_login_post" + }, + "Body_upload_stp_api_upload_post": { + "properties": { + "file": { + "type": "string", + "contentMediaType": "application/octet-stream", + "title": "File" + }, + "material": { + "type": "string", + "title": "Material" + }, + "draft_angle": { + "type": "number", + "title": "Draft Angle" + }, + "shrinkage_rate": { + "type": "number", + "title": "Shrinkage Rate" + }, + "parting_precision": { + "type": "number", + "title": "Parting Precision" + }, + "cavity_match": { + "type": "integer", + "title": "Cavity Match" + } + }, + "type": "object", + "required": [ + "file", + "material", + "draft_angle", + "shrinkage_rate", + "parting_precision", + "cavity_match" + ], + "title": "Body_upload_stp_api_upload_post" + }, + "CustomerCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "CustomerCreate" + }, + "CustomerResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "contact_person", + "phone", + "email", + "is_active" + ], + "title": "CustomerResponse" + }, + "FinanceAllocationCreate": { + "properties": { + "order_type": { + "type": "string", + "enum": [ + "sales", + "purchase" + ], + "title": "Order Type" + }, + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "allocated_amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Allocated Amount" + } + }, + "type": "object", + "required": [ + "order_type", + "order_id", + "allocated_amount" + ], + "title": "FinanceAllocationCreate" + }, + "FinanceAllocationResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_type": { + "type": "string", + "title": "Order Type" + }, + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "allocated_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Allocated Amount" + } + }, + "type": "object", + "required": [ + "id", + "order_type", + "order_id", + "allocated_amount" + ], + "title": "FinanceAllocationResponse" + }, + "FinancePartnerProductStatementResponse": { + "properties": { + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "year": { + "type": "integer", + "title": "Year" + }, + "quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "partner_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Partner Id" + }, + "order_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Amount Total" + }, + "settled_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Amount Total" + }, + "outstanding_amount_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Amount Total" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PartnerProductStatementItemResponse" + }, + "type": "array", + "title": "Items", + "default": [] + } + }, + "type": "object", + "required": [ + "partner_type", + "year", + "period_label", + "order_amount_total", + "settled_amount_total", + "outstanding_amount_total" + ], + "title": "FinancePartnerProductStatementResponse" + }, + "FinancePartnerStatementResponse": { + "properties": { + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "year": { + "type": "integer", + "title": "Year" + }, + "quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "order_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Total" + }, + "settled_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Total" + }, + "transaction_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Transaction Total" + }, + "outstanding_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Total" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PartnerStatementItemResponse" + }, + "type": "array", + "title": "Items", + "default": [] + } + }, + "type": "object", + "required": [ + "partner_type", + "year", + "period_label", + "order_total", + "settled_total", + "transaction_total", + "outstanding_total" + ], + "title": "FinancePartnerStatementResponse" + }, + "FinanceSummaryResponse": { + "properties": { + "receivable_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Receivable Total" + }, + "payable_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Payable Total" + }, + "monthly_receipt_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Monthly Receipt Total" + }, + "monthly_payment_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Monthly Payment Total" + }, + "selected_year": { + "type": "integer", + "title": "Selected Year" + }, + "selected_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Selected Quarter" + }, + "period_label": { + "type": "string", + "title": "Period Label" + }, + "period_receipt_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Period Receipt Total", + "default": "0" + }, + "period_payment_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Period Payment Total", + "default": "0" + }, + "overdue_receivable_count": { + "type": "integer", + "title": "Overdue Receivable Count", + "default": 0 + }, + "overdue_payable_count": { + "type": "integer", + "title": "Overdue Payable Count", + "default": 0 + } + }, + "type": "object", + "required": [ + "receivable_total", + "payable_total", + "monthly_receipt_total", + "monthly_payment_total", + "selected_year", + "period_label" + ], + "title": "FinanceSummaryResponse" + }, + "FinanceTransactionResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "txn_no": { + "type": "string", + "title": "Txn No" + }, + "txn_type": { + "type": "string", + "enum": [ + "receipt", + "payment" + ], + "title": "Txn Type" + }, + "partner_type": { + "type": "string", + "enum": [ + "customer", + "supplier" + ], + "title": "Partner Type" + }, + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Partner Name" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "txn_date": { + "type": "string", + "format": "date-time", + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "status": { + "type": "string", + "enum": [ + "confirmed", + "voided" + ], + "title": "Status" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationResponse" + }, + "type": "array", + "title": "Allocations", + "default": [] + } + }, + "type": "object", + "required": [ + "id", + "txn_no", + "txn_type", + "partner_type", + "partner_id", + "amount", + "txn_date", + "method", + "account_name", + "status", + "remark", + "created_at" + ], + "title": "FinanceTransactionResponse" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "InventoryCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity", + "default": "0" + }, + "locked_quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Locked Quantity", + "default": "0" + }, + "batch_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Batch Number" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Location" + } + }, + "type": "object", + "required": [ + "product_id", + "warehouse_id" + ], + "title": "InventoryCreate" + }, + "InventoryResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "warehouse_name": { + "type": "string", + "title": "Warehouse Name" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "locked_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Locked Quantity" + }, + "available_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Available Quantity" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_name", + "product_sku", + "warehouse_id", + "warehouse_name", + "quantity", + "locked_quantity", + "available_quantity" + ], + "title": "InventoryResponse" + }, + "InventoryUpdate": { + "properties": { + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Quantity" + }, + "locked_quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Locked Quantity" + }, + "batch_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Batch Number" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Location" + } + }, + "type": "object", + "title": "InventoryUpdate" + }, + "LoginRequest": { + "properties": { + "username": { + "type": "string", + "title": "Username" + }, + "password": { + "type": "string", + "title": "Password" + } + }, + "type": "object", + "required": [ + "username", + "password" + ], + "title": "LoginRequest" + }, + "MaterialConsumptionItem": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "quantity": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Quantity" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "material_id", + "quantity" + ], + "title": "MaterialConsumptionItem" + }, + "MaterialConsumptionRequest": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/MaterialConsumptionItem" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "MaterialConsumptionRequest" + }, + "MaterialPriceHistoryCreate": { + "properties": { + "price": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Price", + "description": "物料价格" + }, + "supplier_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Supplier Id", + "description": "供应商ID" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark", + "description": "备注" + } + }, + "type": "object", + "required": [ + "price" + ], + "title": "MaterialPriceHistoryCreate", + "description": "创建物料价格历史的请求模型" + }, + "MaterialPriceHistoryResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "price": { + "type": "number", + "title": "Price" + }, + "effective_date": { + "type": "string", + "format": "date-time", + "title": "Effective Date" + }, + "supplier_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Supplier Id" + }, + "supplier_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Supplier Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "price", + "effective_date", + "supplier_id", + "supplier_name", + "remark", + "created_at" + ], + "title": "MaterialPriceHistoryResponse", + "description": "物料价格历史的响应模型" + }, + "MaterialPriceTrendResponse": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "current_price": { + "type": "number", + "title": "Current Price" + }, + "price_change": { + "type": "number", + "title": "Price Change" + }, + "price_change_percent": { + "type": "number", + "title": "Price Change Percent" + }, + "price_history": { + "items": { + "$ref": "#/components/schemas/PriceHistoryItem" + }, + "type": "array", + "title": "Price History" + } + }, + "type": "object", + "required": [ + "product_id", + "product_sku", + "product_name", + "current_price", + "price_change", + "price_change_percent", + "price_history" + ], + "title": "MaterialPriceTrendResponse", + "description": "物料价格趋势的响应模型" + }, + "MaterialSupplierCreate": { + "properties": { + "supplier_id": { + "type": "integer", + "title": "Supplier Id", + "description": "供应商ID" + }, + "is_primary": { + "type": "boolean", + "title": "Is Primary", + "description": "是否为主要供应商", + "default": false + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person", + "description": "联系人" + }, + "contact_phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Phone", + "description": "联系电话" + }, + "lead_time": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Lead Time", + "description": "交货周期(天)" + }, + "min_order_quantity": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Min Order Quantity", + "description": "最小订购量" + } + }, + "type": "object", + "required": [ + "supplier_id" + ], + "title": "MaterialSupplierCreate", + "description": "创建物料供应商关联的请求模型" + }, + "MaterialSupplierResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "is_primary": { + "type": "boolean", + "title": "Is Primary" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "contact_phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Phone" + }, + "lead_time": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Lead Time" + }, + "min_order_quantity": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Min Order Quantity" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "supplier_id", + "supplier_name", + "is_primary", + "contact_person", + "contact_phone", + "lead_time", + "min_order_quantity", + "created_at", + "updated_at" + ], + "title": "MaterialSupplierResponse", + "description": "物料供应商关联的响应模型" + }, + "PaginatedResponse_FinanceTransactionResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/FinanceTransactionResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[FinanceTransactionResponse]" + }, + "PaginatedResponse_InventoryResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/InventoryResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[InventoryResponse]" + }, + "PaginatedResponse_PurchaseOrderResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[PurchaseOrderResponse]" + }, + "PaginatedResponse_SalesOrderResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[SalesOrderResponse]" + }, + "PaginatedResponse_StockMovementResponse_": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/StockMovementResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + }, + "skip": { + "type": "integer", + "title": "Skip" + }, + "limit": { + "type": "integer", + "title": "Limit" + } + }, + "type": "object", + "required": [ + "items", + "total", + "skip", + "limit" + ], + "title": "PaginatedResponse[StockMovementResponse]" + }, + "PartnerProductStatementItemResponse": { + "properties": { + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "type": "string", + "title": "Partner Name" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "order_count": { + "type": "integer", + "title": "Order Count" + }, + "order_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Quantity" + }, + "order_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Amount" + }, + "settled_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Amount" + }, + "outstanding_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Amount" + }, + "period_year": { + "type": "integer", + "title": "Period Year" + }, + "period_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Period Quarter" + } + }, + "type": "object", + "required": [ + "partner_id", + "partner_name", + "product_id", + "product_name", + "order_count", + "order_quantity", + "order_amount", + "settled_amount", + "outstanding_amount", + "period_year" + ], + "title": "PartnerProductStatementItemResponse" + }, + "PartnerStatementItemResponse": { + "properties": { + "partner_id": { + "type": "integer", + "title": "Partner Id" + }, + "partner_name": { + "type": "string", + "title": "Partner Name" + }, + "order_count": { + "type": "integer", + "title": "Order Count" + }, + "transaction_count": { + "type": "integer", + "title": "Transaction Count" + }, + "order_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Order Total" + }, + "settled_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Settled Total" + }, + "transaction_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Transaction Total" + }, + "outstanding_total": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Outstanding Total" + }, + "period_year": { + "type": "integer", + "title": "Period Year" + }, + "period_quarter": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Period Quarter" + } + }, + "type": "object", + "required": [ + "partner_id", + "partner_name", + "order_count", + "transaction_count", + "order_total", + "settled_total", + "transaction_total", + "outstanding_total", + "period_year" + ], + "title": "PartnerStatementItemResponse" + }, + "PayableItemResponse": { + "properties": { + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "payable_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Payable Amount" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "order_id", + "order_no", + "supplier_id", + "supplier_name", + "order_date", + "total_amount", + "paid_amount", + "payable_amount", + "status" + ], + "title": "PayableItemResponse" + }, + "PaymentCreate": { + "properties": { + "amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Amount" + }, + "txn_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method", + "default": "bank" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationCreate" + }, + "type": "array", + "title": "Allocations" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + } + }, + "type": "object", + "required": [ + "amount", + "allocations", + "supplier_id" + ], + "title": "PaymentCreate" + }, + "PermissionCreate": { + "properties": { + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Module" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "code", + "name" + ], + "title": "PermissionCreate" + }, + "PermissionResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Module" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "module", + "description" + ], + "title": "PermissionResponse" + }, + "PriceHistoryItem": { + "properties": { + "date": { + "type": "string", + "format": "date-time", + "title": "Date" + }, + "price": { + "type": "number", + "title": "Price" + }, + "supplier_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Supplier Name" + } + }, + "type": "object", + "required": [ + "date", + "price", + "supplier_name" + ], + "title": "PriceHistoryItem", + "description": "价格历史项" + }, + "ProductBOMResponse": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "total_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Material Cost" + }, + "items": { + "items": { + "$ref": "#/components/schemas/ProductMaterialItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "product_id", + "product_name", + "total_material_cost", + "items" + ], + "title": "ProductBOMResponse" + }, + "ProductBOMUpdate": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/ProductMaterialItemUpdate" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "ProductBOMUpdate" + }, + "ProductCreate": { + "properties": { + "sku": { + "type": "string", + "title": "Sku" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + }, + "unit": { + "type": "string", + "title": "Unit", + "default": "件" + }, + "item_type": { + "type": "string", + "title": "Item Type", + "default": "finished" + }, + "cost_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Cost Price", + "default": "0" + }, + "sale_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Sale Price", + "default": "0" + }, + "min_stock": { + "type": "integer", + "title": "Min Stock", + "default": 0 + }, + "max_stock": { + "type": "integer", + "title": "Max Stock", + "default": 1000 + } + }, + "type": "object", + "required": [ + "sku", + "name" + ], + "title": "ProductCreate" + }, + "ProductMaterialItemResponse": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "material_sku": { + "type": "string", + "title": "Material Sku" + }, + "material_name": { + "type": "string", + "title": "Material Name" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "unit_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Cost" + }, + "line_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Line Cost" + } + }, + "type": "object", + "required": [ + "material_id", + "material_sku", + "material_name", + "quantity", + "unit_cost", + "line_cost" + ], + "title": "ProductMaterialItemResponse" + }, + "ProductMaterialItemUpdate": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity" + }, + "loss_rate": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Loss Rate", + "default": "0" + } + }, + "type": "object", + "required": [ + "material_id", + "quantity" + ], + "title": "ProductMaterialItemUpdate" + }, + "ProductResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "sku": { + "type": "string", + "title": "Sku" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" + }, + "unit": { + "type": "string", + "title": "Unit" + }, + "item_type": { + "type": "string", + "title": "Item Type" + }, + "cost_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Price" + }, + "sale_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Sale Price" + }, + "min_stock": { + "type": "integer", + "title": "Min Stock" + }, + "max_stock": { + "type": "integer", + "title": "Max Stock" + }, + "material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Material Cost", + "default": "0" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "sku", + "name", + "description", + "category", + "unit", + "item_type", + "cost_price", + "sale_price", + "min_stock", + "max_stock", + "is_active", + "created_at" + ], + "title": "ProductResponse" + }, + "ProductionMaterialPlanItemResponse": { + "properties": { + "material_id": { + "type": "integer", + "title": "Material Id" + }, + "material_sku": { + "type": "string", + "title": "Material Sku" + }, + "material_name": { + "type": "string", + "title": "Material Name" + }, + "required_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Required Quantity" + }, + "available_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Available Quantity" + }, + "shortage_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Shortage Quantity" + }, + "unit_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Cost" + }, + "required_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Required Cost" + } + }, + "type": "object", + "required": [ + "material_id", + "material_sku", + "material_name", + "required_quantity", + "available_quantity", + "shortage_quantity", + "unit_cost", + "required_cost" + ], + "title": "ProductionMaterialPlanItemResponse" + }, + "PurchaseOrderCreate": { + "properties": { + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderItemCreate" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "supplier_id", + "items" + ], + "title": "PurchaseOrderCreate" + }, + "PurchaseOrderDetailResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "receipt_status": { + "type": "string", + "title": "Receipt Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "received_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Received Date" + }, + "paid_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Paid Date" + }, + "supplier_id": { + "type": "integer", + "title": "Supplier Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "supplier_name", + "order_date", + "expected_date", + "status", + "receipt_status", + "payment_status", + "total_amount", + "paid_amount", + "remark", + "created_at", + "received_date", + "paid_date", + "supplier_id", + "items" + ], + "title": "PurchaseOrderDetailResponse" + }, + "PurchaseOrderItemCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Quantity", + "description": "采购数量" + }, + "unit_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Unit Price", + "description": "单价(可选,不填则使用物料成本价格)" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "product_id", + "quantity" + ], + "title": "PurchaseOrderItemCreate" + }, + "PurchaseOrderItemResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "type": "string", + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "quantity": { + "type": "integer", + "title": "Quantity" + }, + "received_quantity": { + "type": "integer", + "title": "Received Quantity" + }, + "unit_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Price" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "quantity", + "received_quantity", + "unit_price", + "amount" + ], + "title": "PurchaseOrderItemResponse" + }, + "PurchaseOrderReceiveItem": { + "properties": { + "item_id": { + "type": "integer", + "title": "Item Id" + }, + "receive_quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Receive Quantity" + } + }, + "type": "object", + "required": [ + "item_id", + "receive_quantity" + ], + "title": "PurchaseOrderReceiveItem" + }, + "PurchaseOrderReceiveRequest": { + "properties": { + "warehouse_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Warehouse Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/PurchaseOrderReceiveItem" + }, + "type": "array", + "minItems": 1, + "title": "Items" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "items" + ], + "title": "PurchaseOrderReceiveRequest" + }, + "PurchaseOrderResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "supplier_name": { + "type": "string", + "title": "Supplier Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "expected_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Expected Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "receipt_status": { + "type": "string", + "title": "Receipt Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "paid_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Paid Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "received_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Received Date" + }, + "paid_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Paid Date" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "supplier_name", + "order_date", + "expected_date", + "status", + "receipt_status", + "payment_status", + "total_amount", + "paid_amount", + "remark", + "created_at", + "received_date", + "paid_date" + ], + "title": "PurchaseOrderResponse" + }, + "ReceiptCreate": { + "properties": { + "amount": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Amount" + }, + "txn_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Txn Date" + }, + "method": { + "type": "string", + "title": "Method", + "default": "bank" + }, + "account_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Account Name" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "allocations": { + "items": { + "$ref": "#/components/schemas/FinanceAllocationCreate" + }, + "type": "array", + "title": "Allocations" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + } + }, + "type": "object", + "required": [ + "amount", + "allocations", + "customer_id" + ], + "title": "ReceiptCreate" + }, + "ReceivableItemResponse": { + "properties": { + "order_id": { + "type": "integer", + "title": "Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "receivable_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Receivable Amount" + }, + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "order_id", + "order_no", + "customer_id", + "customer_name", + "order_date", + "total_amount", + "received_amount", + "receivable_amount", + "status" + ], + "title": "ReceivableItemResponse" + }, + "RoleCreate": { + "properties": { + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "type": "object", + "required": [ + "code", + "name" + ], + "title": "RoleCreate" + }, + "RoleResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "type": "string", + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "is_system": { + "type": "boolean", + "title": "Is System" + }, + "permissions": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Permissions" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "description", + "is_system", + "permissions" + ], + "title": "RoleResponse" + }, + "SalesOrderCreate": { + "properties": { + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderItemCreate" + }, + "type": "array", + "minItems": 1, + "title": "Items" + } + }, + "type": "object", + "required": [ + "customer_id", + "items" + ], + "title": "SalesOrderCreate" + }, + "SalesOrderDetailResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "manufacturing_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Manufacturing Date" + }, + "actual_delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Delivery Date" + }, + "actual_payment_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Payment Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "delivery_status": { + "type": "string", + "title": "Delivery Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "production_status": { + "type": "string", + "title": "Production Status" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "customer_id": { + "type": "integer", + "title": "Customer Id" + }, + "items": { + "items": { + "$ref": "#/components/schemas/SalesOrderItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "customer_name", + "order_date", + "delivery_date", + "manufacturing_date", + "actual_delivery_date", + "actual_payment_date", + "status", + "delivery_status", + "payment_status", + "production_status", + "production_no", + "planned_material_cost", + "actual_material_cost", + "total_amount", + "received_amount", + "remark", + "created_at", + "customer_id", + "items" + ], + "title": "SalesOrderDetailResponse" + }, + "SalesOrderIssueRequest": { + "properties": { + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "warehouse_id" + ], + "title": "SalesOrderIssueRequest" + }, + "SalesOrderIssueResponse": { + "properties": { + "sales_order_id": { + "type": "integer", + "title": "Sales Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "production_no": { + "type": "string", + "title": "Production No" + }, + "movement_count": { + "type": "integer", + "title": "Movement Count" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "cost_deviation": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Deviation" + }, + "cost_deviation_rate": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Cost Deviation Rate" + }, + "production_status": { + "type": "string", + "title": "Production Status" + } + }, + "type": "object", + "required": [ + "sales_order_id", + "order_no", + "production_no", + "movement_count", + "planned_material_cost", + "actual_material_cost", + "cost_deviation", + "cost_deviation_rate", + "production_status" + ], + "title": "SalesOrderIssueResponse" + }, + "SalesOrderItemCreate": { + "properties": { + "product_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Name" + }, + "product_category": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Category" + }, + "product_unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Unit", + "default": "件" + }, + "quantity": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Quantity" + }, + "unit_price": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Unit Price" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "quantity", + "unit_price" + ], + "title": "SalesOrderItemCreate" + }, + "SalesOrderItemResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "quantity": { + "type": "integer", + "title": "Quantity" + }, + "delivered_quantity": { + "type": "integer", + "title": "Delivered Quantity" + }, + "unit_price": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Unit Price" + }, + "amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "quantity", + "delivered_quantity", + "unit_price", + "amount" + ], + "title": "SalesOrderItemResponse" + }, + "SalesOrderProductionPlanResponse": { + "properties": { + "sales_order_id": { + "type": "integer", + "title": "Sales Order Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "production_no": { + "type": "string", + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "items": { + "items": { + "$ref": "#/components/schemas/ProductionMaterialPlanItemResponse" + }, + "type": "array", + "title": "Items" + } + }, + "type": "object", + "required": [ + "sales_order_id", + "order_no", + "customer_name", + "production_no", + "planned_material_cost", + "items" + ], + "title": "SalesOrderProductionPlanResponse" + }, + "SalesOrderResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "order_no": { + "type": "string", + "title": "Order No" + }, + "customer_name": { + "type": "string", + "title": "Customer Name" + }, + "order_date": { + "type": "string", + "format": "date-time", + "title": "Order Date" + }, + "delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Delivery Date" + }, + "manufacturing_date": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Manufacturing Date" + }, + "actual_delivery_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Delivery Date" + }, + "actual_payment_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Actual Payment Date" + }, + "status": { + "type": "string", + "title": "Status" + }, + "delivery_status": { + "type": "string", + "title": "Delivery Status" + }, + "payment_status": { + "type": "string", + "title": "Payment Status" + }, + "production_status": { + "type": "string", + "title": "Production Status" + }, + "production_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Production No" + }, + "planned_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Planned Material Cost" + }, + "actual_material_cost": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Actual Material Cost" + }, + "total_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Total Amount" + }, + "received_amount": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Received Amount" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "order_no", + "customer_name", + "order_date", + "delivery_date", + "manufacturing_date", + "actual_delivery_date", + "actual_payment_date", + "status", + "delivery_status", + "payment_status", + "production_status", + "production_no", + "planned_material_cost", + "actual_material_cost", + "total_amount", + "received_amount", + "remark", + "created_at" + ], + "title": "SalesOrderResponse" + }, + "SalesOrderStatusUpdate": { + "properties": { + "status": { + "type": "string", + "title": "Status" + } + }, + "type": "object", + "required": [ + "status" + ], + "title": "SalesOrderStatusUpdate" + }, + "StockMovementCreate": { + "properties": { + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "warehouse_id": { + "type": "integer", + "title": "Warehouse Id" + }, + "movement_type": { + "type": "string", + "title": "Movement Type" + }, + "quantity": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + } + ], + "title": "Quantity" + }, + "unit_price": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$" + }, + { + "type": "null" + } + ], + "title": "Unit Price" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + } + }, + "type": "object", + "required": [ + "product_id", + "warehouse_id", + "movement_type", + "quantity" + ], + "title": "StockMovementCreate" + }, + "StockMovementResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "product_id": { + "type": "integer", + "title": "Product Id" + }, + "product_sku": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Product Sku" + }, + "product_name": { + "type": "string", + "title": "Product Name" + }, + "movement_type": { + "type": "string", + "title": "Movement Type" + }, + "quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Quantity" + }, + "before_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "Before Quantity" + }, + "after_quantity": { + "type": "string", + "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$", + "title": "After Quantity" + }, + "reference_no": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Reference No" + }, + "remark": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remark" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "product_id", + "product_sku", + "product_name", + "movement_type", + "quantity", + "before_quantity", + "after_quantity", + "reference_no", + "remark", + "created_at" + ], + "title": "StockMovementResponse" + }, + "SupplierCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "SupplierCreate" + }, + "SupplierResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "contact_person": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Contact Person" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "contact_person", + "phone", + "email", + "is_active" + ], + "title": "SupplierResponse" + }, + "Token": { + "properties": { + "access_token": { + "type": "string", + "title": "Access Token" + }, + "token_type": { + "type": "string", + "title": "Token Type" + }, + "user": { + "$ref": "#/components/schemas/UserResponse" + } + }, + "type": "object", + "required": [ + "access_token", + "token_type", + "user" + ], + "title": "Token" + }, + "UserCreate": { + "properties": { + "username": { + "type": "string", + "title": "Username" + }, + "email": { + "type": "string", + "title": "Email" + }, + "password": { + "type": "string", + "title": "Password" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "role_ids": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Role Ids", + "default": [] + } + }, + "type": "object", + "required": [ + "username", + "email", + "password" + ], + "title": "UserCreate" + }, + "UserResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "username": { + "type": "string", + "title": "Username" + }, + "email": { + "type": "string", + "title": "Email" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "is_superuser": { + "type": "boolean", + "title": "Is Superuser", + "default": false + }, + "roles": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Roles" + } + }, + "type": "object", + "required": [ + "id", + "username", + "email", + "full_name", + "is_active", + "roles" + ], + "title": "UserResponse" + }, + "UserUpdate": { + "properties": { + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "is_active": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Active" + }, + "role_ids": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Role Ids" + } + }, + "type": "object", + "title": "UserUpdate" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + }, + "input": { + "title": "Input" + }, + "ctx": { + "type": "object", + "title": "Context" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "WarehouseCreate": { + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + }, + "manager": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Manager" + }, + "phone": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Phone" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "WarehouseCreate" + }, + "WarehouseResponse": { + "properties": { + "id": { + "type": "integer", + "title": "Id" + }, + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code" + }, + "name": { + "type": "string", + "title": "Name" + }, + "address": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Address" + }, + "manager": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Manager" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "is_default": { + "type": "boolean", + "title": "Is Default" + } + }, + "type": "object", + "required": [ + "id", + "code", + "name", + "address", + "manager", + "is_active", + "is_default" + ], + "title": "WarehouseResponse" + } + }, + "securitySchemes": { + "OAuth2PasswordBearer": { + "type": "oauth2", + "flows": { + "password": { + "scopes": {}, + "tokenUrl": "/api/auth/login" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/inventory/api/inventory_routes.py b/src/inventory/api/inventory_routes.py index 5d8e8ab..2858164 100644 --- a/src/inventory/api/inventory_routes.py +++ b/src/inventory/api/inventory_routes.py @@ -1,22 +1,17 @@ -""" -库存管理路由模块 - -提供库存信息的查询功能,包括: -- 库存列表查询(支持分页、仓库筛选、产品筛选、低库存筛选) -- 显示产品库存数量、锁定数量、可用数量等信息 +"""库存管理路由层 - 薄路由 +业务逻辑下沉至 inventory.services.inventory_service,路由只做参数校验与响应组装。 路由前缀: /api/inventory """ -from fastapi import APIRouter, Depends, Query, HTTPException +from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, update, func -from sqlalchemy.exc import IntegrityError -from typing import Optional, List +from typing import Optional from shared.database.database import get_db_session from shared.services.auth_service import get_current_active_user -from shared.models.database import User, Product, Warehouse, Inventory +from shared.models.database import User from ..schemas import InventoryResponse, InventoryCreate, InventoryUpdate, PaginatedResponse +from ..services.inventory_service import inventory_service router = APIRouter(prefix="/inventory", tags=["库存管理"]) @@ -31,43 +26,7 @@ async def list_inventory( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - base_query = ( - select(Inventory, Product, Warehouse) - .join(Product, Inventory.product_id == Product.id) - .join(Warehouse, Inventory.warehouse_id == Warehouse.id) - .where(Product.is_active == True) - .where(Product.item_type == "material") - .where(Warehouse.is_active == True) - ) - - if warehouse_id: - base_query = base_query.where(Inventory.warehouse_id == warehouse_id) - if product_id: - base_query = base_query.where(Inventory.product_id == product_id) - if low_stock: - base_query = base_query.where(Inventory.quantity <= Product.min_stock) - - count_query = select(func.count()).select_from(base_query.subquery()) - total = await db_session.scalar(count_query) or 0 - - query = base_query.offset(skip).limit(limit) - result = await db_session.execute(query) - - inventory_list = [] - for inv, product, warehouse in result.all(): - inventory_list.append(InventoryResponse( - id=inv.id, - product_id=inv.product_id, - product_name=product.name, - product_sku=product.sku, - warehouse_id=inv.warehouse_id, - warehouse_name=warehouse.name, - quantity=inv.quantity, - locked_quantity=inv.locked_quantity, - available_quantity=inv.available_quantity - )) - - return PaginatedResponse(items=inventory_list, total=total, skip=skip, limit=limit) + return await inventory_service.list_inventory(db_session, warehouse_id, product_id, low_stock, skip, limit) @router.post("", response_model=InventoryResponse, status_code=201) @@ -76,64 +35,7 @@ async def create_inventory( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - if payload.quantity < 0 or payload.locked_quantity < 0: - raise HTTPException(status_code=400, detail="库存数量不能为负数") - if payload.locked_quantity > payload.quantity: - raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") - - product_result = await db_session.execute( - select(Product).where(Product.id == payload.product_id, Product.is_active == True) - ) - product = product_result.scalar_one_or_none() - if not product: - raise HTTPException(status_code=404, detail="物料不存在") - if product.item_type != "material": - raise HTTPException(status_code=400, detail="库存仅支持物料") - - warehouse_result = await db_session.execute( - select(Warehouse).where(Warehouse.id == payload.warehouse_id, Warehouse.is_active == True) - ) - warehouse = warehouse_result.scalar_one_or_none() - if not warehouse: - raise HTTPException(status_code=404, detail="仓库不存在") - - exists_result = await db_session.execute( - select(Inventory).where( - Inventory.product_id == payload.product_id, - Inventory.warehouse_id == payload.warehouse_id - ) - ) - if exists_result.scalar_one_or_none(): - raise HTTPException(status_code=400, detail="该仓库已存在该物料库存记录") - - inventory = Inventory( - product_id=payload.product_id, - warehouse_id=payload.warehouse_id, - quantity=payload.quantity, - locked_quantity=payload.locked_quantity, - batch_number=payload.batch_number, - location=payload.location - ) - db_session.add(inventory) - try: - await db_session.commit() - except IntegrityError: - # 并发创建命中 (product_id, warehouse_id) 唯一约束 - await db_session.rollback() - raise HTTPException(status_code=400, detail="该仓库已存在该物料库存记录") - await db_session.refresh(inventory) - - return InventoryResponse( - id=inventory.id, - product_id=product.id, - product_name=product.name, - product_sku=product.sku, - warehouse_id=warehouse.id, - warehouse_name=warehouse.name, - quantity=inventory.quantity, - locked_quantity=inventory.locked_quantity, - available_quantity=inventory.available_quantity - ) + return await inventory_service.create_inventory(db_session, payload, current_user) @router.put("/{inventory_id}", response_model=InventoryResponse) @@ -143,48 +45,7 @@ async def update_inventory( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - result = await db_session.execute( - select(Inventory, Product, Warehouse) - .join(Product, Inventory.product_id == Product.id) - .join(Warehouse, Inventory.warehouse_id == Warehouse.id) - .where(Inventory.id == inventory_id) - .where(Product.item_type == "material") - .with_for_update(of=Inventory) - ) - row = result.first() - if not row: - raise HTTPException(status_code=404, detail="库存记录不存在") - inventory, product, warehouse = row - - if payload.quantity is not None: - if payload.quantity < 0: - raise HTTPException(status_code=400, detail="库存数量不能为负数") - inventory.quantity = payload.quantity - if payload.locked_quantity is not None: - if payload.locked_quantity < 0: - raise HTTPException(status_code=400, detail="锁定数量不能为负数") - inventory.locked_quantity = payload.locked_quantity - if inventory.locked_quantity > inventory.quantity: - raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") - - if payload.batch_number is not None: - inventory.batch_number = payload.batch_number - if payload.location is not None: - inventory.location = payload.location - - await db_session.commit() - await db_session.refresh(inventory) - return InventoryResponse( - id=inventory.id, - product_id=product.id, - product_name=product.name, - product_sku=product.sku, - warehouse_id=warehouse.id, - warehouse_name=warehouse.name, - quantity=inventory.quantity, - locked_quantity=inventory.locked_quantity, - available_quantity=inventory.available_quantity - ) + return await inventory_service.update_inventory(db_session, inventory_id, payload, current_user) @router.delete("/{inventory_id}") @@ -193,12 +54,4 @@ async def delete_inventory( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - result = await db_session.execute(select(Inventory).where(Inventory.id == inventory_id)) - inventory = result.scalar_one_or_none() - if not inventory: - raise HTTPException(status_code=404, detail="库存记录不存在") - if inventory.quantity > 0: - raise HTTPException(status_code=400, detail="库存数量不为零,无法删除库存记录") - await db_session.delete(inventory) - await db_session.commit() - return {"message": "库存记录已删除"} + return await inventory_service.delete_inventory(db_session, inventory_id, current_user) diff --git a/src/inventory/api/purchase_order_routes.py b/src/inventory/api/purchase_order_routes.py index f8c169e..3d7d1b9 100644 --- a/src/inventory/api/purchase_order_routes.py +++ b/src/inventory/api/purchase_order_routes.py @@ -1,203 +1,27 @@ -""" -采购订单路由模块 - -提供采购订单的管理功能,包括: -- 采购订单列表查询(支持分页、状态筛选) -- 创建采购订单(自动生成订单号、计算总金额) -- 采购订单明细管理 +"""采购订单路由层 - 薄路由 +业务逻辑下沉至 inventory.services.purchase_order_service,路由只做参数校验与响应组装。 路由前缀: /api/purchase-orders """ -from fastapi import APIRouter, Depends, Query, HTTPException +from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, func, update -from typing import Optional, List -from decimal import Decimal +from typing import Optional from shared.database.database import get_db_session from shared.services.auth_service import get_current_active_user -from shared.models.database import ( - User, - Supplier, - Product, - Warehouse, - Inventory, - StockMovement, - PurchaseOrder, - PurchaseOrderItem -) +from shared.models.database import User from ..schemas import ( PurchaseOrderCreate, PurchaseOrderResponse, PurchaseOrderDetailResponse, - PurchaseOrderItemResponse, PurchaseOrderReceiveRequest, PaginatedResponse, ) -from ..utils import generate_order_no +from ..services.purchase_order_service import purchase_order_service router = APIRouter(prefix="/purchase-orders", tags=["采购订单"]) -def _compute_receipt_status(order: PurchaseOrder) -> str: - """收货状态:已下单 / 部分收货 / 已收货 / 已作废""" - if order.status == "cancelled": - return "cancelled" - if order.status in ("received", "paid"): - return "received" - if order.status == "partial_received": - return "partial_received" - return "pending" - - -def _compute_payment_status(order: PurchaseOrder) -> str: - """付款状态:未付款 / 已付款""" - if order.status == "paid" or order.paid_date: - return "paid" - return "unpaid" - - -def _build_purchase_order_response(order: PurchaseOrder, supplier_name: str) -> PurchaseOrderResponse: - return PurchaseOrderResponse( - id=order.id, - order_no=order.order_no, - supplier_name=supplier_name, - order_date=order.order_date, - expected_date=order.expected_date, - status=order.status, - receipt_status=_compute_receipt_status(order), - payment_status=_compute_payment_status(order), - total_amount=order.total_amount, - paid_amount=order.paid_amount, - remark=order.remark, - created_at=order.created_at, - received_date=order.received_date, - paid_date=order.paid_date - ) - - -async def _get_order_with_supplier( - db_session: AsyncSession, - order_id: int -) -> tuple[PurchaseOrder, Supplier]: - result = await db_session.execute( - select(PurchaseOrder, Supplier) - .join(Supplier, PurchaseOrder.supplier_id == Supplier.id) - .where(PurchaseOrder.id == order_id) - ) - row = result.first() - if not row: - raise HTTPException(status_code=404, detail="采购订单不存在") - return row[0], row[1] - - -async def _build_purchase_order_detail( - db_session: AsyncSession, - order: PurchaseOrder, - supplier_name: str -) -> PurchaseOrderDetailResponse: - item_result = await db_session.execute( - select(PurchaseOrderItem, Product) - .join(Product, PurchaseOrderItem.product_id == Product.id) - .where(PurchaseOrderItem.order_id == order.id) - .order_by(PurchaseOrderItem.id.asc()) - ) - item_rows = item_result.all() - - return PurchaseOrderDetailResponse( - id=order.id, - order_no=order.order_no, - supplier_id=order.supplier_id, - supplier_name=supplier_name, - order_date=order.order_date, - expected_date=order.expected_date, - status=order.status, - receipt_status=_compute_receipt_status(order), - payment_status=_compute_payment_status(order), - total_amount=order.total_amount, - paid_amount=order.paid_amount, - remark=order.remark, - created_at=order.created_at, - received_date=order.received_date, - paid_date=order.paid_date, - items=[ - PurchaseOrderItemResponse( - id=item.id, - product_id=item.product_id, - product_sku=product.sku, - product_name=product.name, - quantity=item.quantity, - received_quantity=item.received_quantity, - unit_price=item.unit_price, - amount=item.amount, - remark=item.remark - ) for item, product in item_rows - ] - ) - - -async def _resolve_receive_warehouse( - db_session: AsyncSession, - warehouse_id: Optional[int] -) -> Warehouse: - if warehouse_id: - result = await db_session.execute( - select(Warehouse).where(Warehouse.id == warehouse_id, Warehouse.is_active == True) - ) - warehouse = result.scalar_one_or_none() - if not warehouse: - raise HTTPException(status_code=404, detail="仓库不存在") - return warehouse - - result = await db_session.execute( - select(Warehouse).where(Warehouse.is_active == True).order_by(Warehouse.is_default.desc(), Warehouse.id.asc()) - ) - warehouse = result.scalars().first() - if not warehouse: - raise HTTPException(status_code=400, detail="未配置可用仓库") - return warehouse - - -async def _apply_order_items( - db_session: AsyncSession, - order: PurchaseOrder, - order_data: PurchaseOrderCreate -) -> Decimal: - total_amount = Decimal("0") - for item_data in order_data.items: - product_result = await db_session.execute( - select(Product).where(Product.id == item_data.product_id, Product.is_active == True) - ) - product = product_result.scalar_one_or_none() - if not product: - raise HTTPException(status_code=400, detail=f"物料不存在: {item_data.product_id}") - if product.item_type != "material": - raise HTTPException(status_code=400, detail=f"采购单仅允许物料: {product.name}") - - # 优先使用前端传入的单价,否则使用物料成本价 - if item_data.unit_price and item_data.unit_price > 0: - unit_price = float(item_data.unit_price) - else: - unit_price = product.cost_price or 0 - if unit_price <= 0: - raise HTTPException(status_code=400, detail=f"物料 {product.name} 未设置价格,请在物料管理界面设置成本价或在采购明细中填写单价") - - try: - item = PurchaseOrderItem( - order_id=order.id, - product_id=item_data.product_id, - quantity=int(item_data.quantity), - unit_price=Decimal(str(unit_price)), - amount=Decimal(str(item_data.quantity)) * Decimal(str(unit_price)), - remark=item_data.remark or None - ) - db_session.add(item) - total_amount += item.amount - except Exception as e: - raise HTTPException(status_code=400, detail=f"创建订单明细失败: {str(e)}") - return total_amount - - @router.get("", response_model=PaginatedResponse[PurchaseOrderResponse]) async def list_purchase_orders( status: Optional[str] = None, @@ -206,26 +30,7 @@ async def list_purchase_orders( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - base_query = ( - select(PurchaseOrder, Supplier) - .join(Supplier, PurchaseOrder.supplier_id == Supplier.id) - .order_by(PurchaseOrder.created_at.desc()) - ) - - if status: - base_query = base_query.where(PurchaseOrder.status == status) - - count_query = select(func.count()).select_from(base_query.subquery()) - total = await db_session.scalar(count_query) or 0 - - query = base_query.offset(skip).limit(limit) - result = await db_session.execute(query) - - orders = [] - for order, supplier in result.all(): - orders.append(_build_purchase_order_response(order, supplier.name)) - - return PaginatedResponse(items=orders, total=total, skip=skip, limit=limit) + return await purchase_order_service.list_orders(db_session, status, skip, limit) @router.post("", response_model=PurchaseOrderResponse, status_code=201) @@ -234,31 +39,7 @@ async def create_purchase_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order = PurchaseOrder( - order_no=generate_order_no("PO"), - supplier_id=order_data.supplier_id, - expected_date=order_data.expected_date, - remark=order_data.remark, - operator_id=current_user.id, - status="pending" - ) - db_session.add(order) - await db_session.flush() - - try: - order.total_amount = await _apply_order_items(db_session, order, order_data) - await db_session.commit() - except (HTTPException, Exception): - await db_session.rollback() - raise - await db_session.refresh(order) - - supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) - supplier = supplier_result.scalar_one_or_none() - if not supplier: - raise HTTPException(status_code=404, detail="供应商不存在") - - return _build_purchase_order_response(order, supplier.name) + return await purchase_order_service.create_order(db_session, order_data, current_user) @router.get("/{order_id}", response_model=PurchaseOrderDetailResponse) @@ -267,8 +48,7 @@ async def get_purchase_order_detail( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, supplier = await _get_order_with_supplier(db_session, order_id) - return await _build_purchase_order_detail(db_session, order, supplier.name) + return await purchase_order_service.get_detail(db_session, order_id) @router.put("/{order_id}", response_model=PurchaseOrderResponse) @@ -278,35 +58,7 @@ async def update_purchase_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, _ = await _get_order_with_supplier(db_session, order_id) - if order.paid_amount and order.paid_amount > 0: - raise HTTPException(status_code=400, detail="已付款采购单不允许修改") - - item_result = await db_session.execute( - select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) - ) - existing_items = item_result.scalars().all() - if any((item.received_quantity or 0) > 0 for item in existing_items): - raise HTTPException(status_code=400, detail="已发生入库的采购单不允许直接修改") - - for item in existing_items: - await db_session.delete(item) - - try: - order.supplier_id = order_data.supplier_id - order.expected_date = order_data.expected_date - order.remark = order_data.remark - order.total_amount = await _apply_order_items(db_session, order, order_data) - order.status = "pending" - - await db_session.commit() - except (HTTPException, Exception): - await db_session.rollback() - raise - await db_session.refresh(order) - supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) - supplier = supplier_result.scalar_one_or_none() - return _build_purchase_order_response(order, supplier.name if supplier else "未知供应商") + return await purchase_order_service.update_order(db_session, order_id, order_data, current_user) @router.delete("/{order_id}") @@ -315,17 +67,7 @@ async def delete_purchase_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, _ = await _get_order_with_supplier(db_session, order_id) - if order.paid_amount and order.paid_amount > 0: - raise HTTPException(status_code=400, detail="已付款采购单不允许删除") - item_result = await db_session.execute( - select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) - ) - if any((item.received_quantity or 0) > 0 for item in item_result.scalars().all()): - raise HTTPException(status_code=400, detail="已发生入库的采购单不允许删除") - await db_session.delete(order) - await db_session.commit() - return {"message": "采购订单已删除"} + return await purchase_order_service.delete_order(db_session, order_id, current_user) @router.patch("/{order_id}/status") @@ -335,45 +77,7 @@ async def update_purchase_order_status( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, _ = await _get_order_with_supplier(db_session, order_id) - - new_status = status.get("status") - if not new_status: - raise HTTPException(status_code=400, detail="状态不能为空") - - valid_statuses = ["pending", "partial_received", "received", "paid", "cancelled"] - if new_status not in valid_statuses: - raise HTTPException(status_code=400, detail=f"无效的状态值,有效值为: {valid_statuses}") - - # 收货状态(partial_received/received)只能由收货端点驱动,禁止手动设置,避免与实际入库脱钩 - if new_status in ("partial_received", "received"): - raise HTTPException(status_code=400, detail="收货状态只能通过收货入库操作自动变更,不能手动设置") - - # 状态转换逻辑 - if order.status == "paid": - raise HTTPException(status_code=400, detail="已付款的采购订单禁止修改状态") - if order.status == "cancelled": - raise HTTPException(status_code=400, detail="已作废的采购订单禁止修改状态") - if order.status == "received" and new_status not in ("paid", "cancelled"): - raise HTTPException(status_code=400, detail="已收货的采购订单只能标记为已付款或已作废") - if order.status == "partial_received" and new_status not in ("received", "paid", "cancelled"): - raise HTTPException(status_code=400, detail="部分收货的采购订单只能标记为已收货、已付款或已作废") - if new_status == "cancelled" and order.status == "paid": - raise HTTPException(status_code=400, detail="已付款的订单不能作废") - - # 更新状态和对应时间 - order.status = new_status - if new_status == "received": - order.received_date = func.now() - elif new_status == "paid": - order.paid_date = func.now() - - await db_session.commit() - await db_session.refresh(order) - - supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) - supplier = supplier_result.scalar_one_or_none() - return _build_purchase_order_response(order, supplier.name if supplier else "未知供应商") + return await purchase_order_service.update_status(db_session, order_id, status, current_user) @router.post("/{order_id}/receive", response_model=PurchaseOrderDetailResponse) @@ -383,89 +87,4 @@ async def receive_purchase_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, supplier = await _get_order_with_supplier(db_session, order_id) - if order.status in ("cancelled", "paid"): - raise HTTPException(status_code=400, detail=f"当前采购单状态为 {order.status},不允许收货") - warehouse = await _resolve_receive_warehouse(db_session, payload.warehouse_id) - - item_result = await db_session.execute( - select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) - ) - item_map = {item.id: item for item in item_result.scalars().all()} - if not item_map: - raise HTTPException(status_code=400, detail="采购单无明细,无法入库") - - if not payload.items: - raise HTTPException(status_code=400, detail="请提供本次入库明细") - - for receive_item in payload.items: - item = item_map.get(receive_item.item_id) - if not item: - raise HTTPException(status_code=400, detail=f"采购明细不存在: {receive_item.item_id}") - if receive_item.receive_quantity <= 0: - raise HTTPException(status_code=400, detail="入库数量必须大于0") - remaining_qty = (item.quantity or 0) - (item.received_quantity or 0) - if receive_item.receive_quantity > remaining_qty: - raise HTTPException(status_code=400, detail=f"明细{item.id}入库超量,剩余可入库{remaining_qty}") - - for receive_item in payload.items: - item = item_map[receive_item.item_id] - product_result = await db_session.execute( - select(Product).where(Product.id == item.product_id) - ) - product = product_result.scalar_one_or_none() - if not product: - raise HTTPException(status_code=400, detail=f"物料不存在: {item.product_id}") - - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == item.product_id) - .where(Inventory.warehouse_id == warehouse.id) - .values(quantity=Inventory.quantity + receive_item.receive_quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - inventory = Inventory( - product_id=item.product_id, - warehouse_id=warehouse.id, - quantity=receive_item.receive_quantity, - locked_quantity=0 - ) - db_session.add(inventory) - await db_session.flush() - before_qty = 0 - after_qty = receive_item.receive_quantity - else: - after_qty = int(after_qty) - before_qty = after_qty - receive_item.receive_quantity - item.received_quantity = (item.received_quantity or 0) + receive_item.receive_quantity - - movement = StockMovement( - product_id=item.product_id, - warehouse_id=warehouse.id, - movement_type="purchase_in", - quantity=receive_item.receive_quantity, - before_quantity=before_qty, - after_quantity=after_qty, - reference_type="purchase_order", - reference_id=order.id, - reference_no=order.order_no, - unit_price=item.unit_price, - total_amount=Decimal(str(item.unit_price * receive_item.receive_quantity)), - remark=payload.remark or f"采购单{order.order_no}到货入库", - operator_id=current_user.id - ) - db_session.add(movement) - - all_received = all((item.received_quantity or 0) >= (item.quantity or 0) for item in item_map.values()) - any_received = any((item.received_quantity or 0) > 0 for item in item_map.values()) - if all_received: - order.status = "received" - order.received_date = func.now() - elif any_received: - order.status = "partial_received" - - await db_session.commit() - await db_session.refresh(order) - return await _build_purchase_order_detail(db_session, order, supplier.name) + return await purchase_order_service.receive_order(db_session, order_id, payload, current_user) diff --git a/src/inventory/api/sales_order_routes.py b/src/inventory/api/sales_order_routes.py index 1794e15..01712f5 100644 --- a/src/inventory/api/sales_order_routes.py +++ b/src/inventory/api/sales_order_routes.py @@ -1,406 +1,29 @@ -""" -销售订单路由模块 - -提供销售订单的管理功能,包括: -- 销售订单列表查询(支持分页、状态筛选) -- 创建销售订单(自动生成订单号、计算总金额) -- 销售订单明细管理 +"""销售订单路由层 - 薄路由 +业务逻辑下沉至 inventory.services.sales_order_service,路由只做参数校验与响应组装。 路由前缀: /api/sales-orders """ -from fastapi import APIRouter, Depends, Query, HTTPException +from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, func, delete, update -from typing import Optional, List -from math import ceil -from pydantic import BaseModel, Field -from decimal import Decimal +from typing import Optional from shared.database.database import get_db_session from shared.services.auth_service import get_current_active_user -from shared.models.database import ( - User, - Customer, - Product, - ProductMaterial, - Warehouse, - Inventory, - StockMovement, - SalesOrder, - SalesOrderItem -) +from shared.models.database import User from ..schemas import ( SalesOrderCreate, SalesOrderResponse, SalesOrderDetailResponse, - SalesOrderItemResponse, SalesOrderProductionPlanResponse, - ProductionMaterialPlanItemResponse, SalesOrderIssueRequest, SalesOrderIssueResponse, SalesOrderStatusUpdate, + MaterialConsumptionRequest, PaginatedResponse, ) -from ..utils import generate_order_no +from ..services.sales_order_service import sales_order_service router = APIRouter(prefix="/sales-orders", tags=["销售订单"]) -VALID_ORDER_STATUSES = {"manufacturing", "delivered", "paid", "cancelled"} -PRODUCTION_STATUSES = {"not_started", "bom_missing", "material_issued", "completed"} - - -def _compute_delivery_status(order: SalesOrder) -> str: - """物流状态:制造中 / 已交付 / 已作废""" - if order.status == "cancelled": - return "cancelled" - if order.status in ("delivered", "paid") or order.actual_delivery_date: - return "delivered" - return "manufacturing" - - -def _compute_payment_status(order: SalesOrder) -> str: - """收款状态:未收款 / 已收款""" - if order.status == "paid" or order.actual_payment_date: - return "paid" - return "unpaid" - - -def _build_sales_order_response(order: SalesOrder, customer_name: str) -> SalesOrderResponse: - return SalesOrderResponse( - id=order.id, - order_no=order.order_no, - customer_name=customer_name, - order_date=order.order_date, - delivery_date=order.delivery_date, - manufacturing_date=order.manufacturing_date, - actual_delivery_date=order.actual_delivery_date, - actual_payment_date=order.actual_payment_date, - status=order.status, - delivery_status=_compute_delivery_status(order), - payment_status=_compute_payment_status(order), - production_status=order.production_status or "not_started", - production_no=order.production_no, - planned_material_cost=Decimal(str(order.planned_material_cost or 0)), - actual_material_cost=Decimal(str(order.actual_material_cost or 0)), - total_amount=Decimal(str(order.total_amount or 0)), - received_amount=Decimal(str(order.received_amount or 0)), - remark=order.remark, - created_at=order.created_at - ) - - -async def _build_sales_order_detail_response( - db_session: AsyncSession, - order: SalesOrder, - customer_name: str -) -> SalesOrderDetailResponse: - items_result = await db_session.execute( - select(SalesOrderItem).where(SalesOrderItem.order_id == order.id).order_by(SalesOrderItem.id.asc()) - ) - items = items_result.scalars().all() - return SalesOrderDetailResponse( - id=order.id, - order_no=order.order_no, - customer_id=order.customer_id, - customer_name=customer_name, - order_date=order.order_date, - delivery_date=order.delivery_date, - manufacturing_date=order.manufacturing_date, - actual_delivery_date=order.actual_delivery_date, - actual_payment_date=order.actual_payment_date, - status=order.status, - delivery_status=_compute_delivery_status(order), - payment_status=_compute_payment_status(order), - production_status=order.production_status or "not_started", - production_no=order.production_no, - planned_material_cost=Decimal(str(order.planned_material_cost or 0)), - actual_material_cost=Decimal(str(order.actual_material_cost or 0)), - total_amount=Decimal(str(order.total_amount or 0)), - received_amount=Decimal(str(order.received_amount or 0)), - remark=order.remark, - created_at=order.created_at, - items=[ - SalesOrderItemResponse( - id=item.id, - product_id=item.product_id, - quantity=item.quantity, - delivered_quantity=item.delivered_quantity, - unit_price=Decimal(str(item.unit_price or 0)), - amount=Decimal(str(item.amount or 0)), - remark=item.remark - ) for item in items - ] - ) - - -async def _get_sales_order_with_customer( - db_session: AsyncSession, - order_id: int, -) -> tuple[SalesOrder, Customer]: - result = await db_session.execute( - select(SalesOrder, Customer) - .join(Customer, SalesOrder.customer_id == Customer.id) - .where(SalesOrder.id == order_id) - ) - row = result.first() - if not row: - raise HTTPException(status_code=404, detail="销售订单不存在") - return row[0], row[1] - - -async def _build_material_plan(db_session: AsyncSession, order: SalesOrder) -> tuple[List[ProductionMaterialPlanItemResponse], float]: - item_result = await db_session.execute( - select(SalesOrderItem).where(SalesOrderItem.order_id == order.id) - ) - order_items = item_result.scalars().all() - if not order_items: - return [], 0 - - finished_ids = list({int(i.product_id) for i in order_items}) - bom_result = await db_session.execute( - select(ProductMaterial, Product) - .join(Product, ProductMaterial.material_product_id == Product.id) - .where(ProductMaterial.finished_product_id.in_(finished_ids)) - .where(Product.is_active == True) - .where(Product.item_type == "material") - ) - bom_rows = bom_result.all() - if not bom_rows: - return [], 0 - - bom_by_finished_id = {} - for bom, material in bom_rows: - bom_by_finished_id.setdefault(int(bom.finished_product_id), []).append((bom, material)) - - required_qty_map = {} - for order_item in order_items: - bom_items = bom_by_finished_id.get(int(order_item.product_id)) or [] - for bom, material in bom_items: - qty = Decimal(str(order_item.quantity)) * Decimal(str(bom.quantity or 0)) * (1 + Decimal(str(bom.loss_rate or 0))) - entry = required_qty_map.setdefault(material.id, {"material": material, "required_qty": Decimal("0")}) - entry["required_qty"] += qty - - if not required_qty_map: - return [], Decimal("0") - - material_ids = list(required_qty_map.keys()) - stock_result = await db_session.execute( - select(Inventory.product_id, func.coalesce(func.sum(Inventory.quantity), 0)) - .where(Inventory.product_id.in_(material_ids)) - .group_by(Inventory.product_id) - ) - stock_map = {row[0]: Decimal(str(row[1] or 0)) for row in stock_result.all()} - - plan_items = [] - planned_material_cost = Decimal("0") - for material_id, entry in required_qty_map.items(): - material = entry["material"] - required_qty = int(ceil(entry["required_qty"])) - available_qty = stock_map.get(material_id, Decimal("0")) - shortage_qty = max(required_qty - int(available_qty), 0) - unit_cost = Decimal(str(material.cost_price or 0)) - required_cost = Decimal(str(required_qty)) * unit_cost - planned_material_cost += required_cost - plan_items.append( - ProductionMaterialPlanItemResponse( - material_id=material.id, - material_sku=material.sku, - material_name=material.name, - required_quantity=Decimal(str(required_qty)), - available_quantity=available_qty, - shortage_quantity=Decimal(str(shortage_qty)), - unit_cost=unit_cost, - required_cost=required_cost, - ) - ) - - plan_items = sorted(plan_items, key=lambda x: (x.shortage_quantity, x.required_cost), reverse=True) - return plan_items, planned_material_cost - - -async def _get_default_warehouse(db_session: AsyncSession) -> Warehouse: - warehouse_result = await db_session.execute( - select(Warehouse).where(Warehouse.is_active == True).order_by(Warehouse.is_default.desc(), Warehouse.id.asc()) - ) - warehouse = warehouse_result.scalars().first() - if not warehouse: - raise HTTPException(status_code=400, detail="未配置可用仓库,无法自动扣减物料") - return warehouse - - -async def _issue_materials_for_order_creation( - db_session: AsyncSession, - order: SalesOrder, - current_user: User -) -> tuple[int, float, float]: - warehouse = await _get_default_warehouse(db_session) - plan_items, planned_material_cost = await _build_material_plan(db_session, order) - if not plan_items: - order.production_no = order.production_no or generate_order_no("WO") - order.production_status = "bom_missing" - order.planned_material_cost = 0 - order.actual_material_cost = 0 - order.status = "manufacturing" - return 0, 0, 0 - shortage_items = [item for item in plan_items if item.shortage_quantity > 0] - if shortage_items: - shortage_text = ",".join([f"{item.material_name} 缺 {item.shortage_quantity}" for item in shortage_items]) - raise HTTPException(status_code=400, detail=f"物料库存不足:{shortage_text}") - - production_no = order.production_no or generate_order_no("WO") - actual_material_cost = Decimal("0") - movement_count = 0 - - for item in plan_items: - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == item.material_id) - .where(Inventory.warehouse_id == warehouse.id) - .where(Inventory.quantity >= item.required_quantity) - .values(quantity=Inventory.quantity - item.required_quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - raise HTTPException(status_code=400, detail=f"{item.material_name} 在默认仓库库存不足") - after_qty = int(after_qty) - before_qty = after_qty + int(item.required_quantity) - total_amount = item.required_quantity * item.unit_cost - actual_material_cost += total_amount - - movement = StockMovement( - product_id=item.material_id, - warehouse_id=warehouse.id, - movement_type="issue_to_production", - quantity=item.required_quantity, - before_quantity=before_qty, - after_quantity=after_qty, - reference_type="sales_order", - reference_id=order.id, - reference_no=production_no, - unit_price=item.unit_cost, - total_amount=Decimal(str(total_amount)), - remark=f"销售单{order.order_no}创建时自动扣减物料", - operator_id=current_user.id - ) - db_session.add(movement) - movement_count += 1 - - order.production_no = production_no - order.production_status = "material_issued" - order.planned_material_cost = planned_material_cost - order.actual_material_cost = actual_material_cost - order.status = "manufacturing" - - return movement_count, planned_material_cost, actual_material_cost - - -async def _rollback_issued_materials( - db_session: AsyncSession, - order: SalesOrder, - current_user: User -): - movement_result = await db_session.execute( - select(StockMovement) - .where(StockMovement.reference_type == "sales_order") - .where(StockMovement.reference_id == order.id) - .where(StockMovement.movement_type == "issue_to_production") - .order_by(StockMovement.id.asc()) - ) - movements = movement_result.scalars().all() - if not movements: - return - - for movement in movements: - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == movement.product_id) - .where(Inventory.warehouse_id == movement.warehouse_id) - .values(quantity=Inventory.quantity + movement.quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - inventory = Inventory( - product_id=movement.product_id, - warehouse_id=movement.warehouse_id, - quantity=movement.quantity, - locked_quantity=0 - ) - db_session.add(inventory) - await db_session.flush() - before_qty = 0 - after_qty = movement.quantity - else: - after_qty = int(after_qty) - before_qty = after_qty - movement.quantity - - revert_movement = StockMovement( - product_id=movement.product_id, - warehouse_id=movement.warehouse_id, - movement_type="return_from_production", - quantity=movement.quantity, - before_quantity=before_qty, - after_quantity=after_qty, - reference_type="sales_order", - reference_id=order.id, - reference_no=order.production_no or order.order_no, - unit_price=movement.unit_price, - total_amount=movement.total_amount, - remark=f"销售单{order.order_no}变更/删除,自动回补物料", - operator_id=current_user.id - ) - db_session.add(revert_movement) - - -async def _apply_order_items( - db_session: AsyncSession, - order: SalesOrder, - order_data: SalesOrderCreate -) -> Decimal: - total_amount = Decimal("0") - for item_data in order_data.items: - product = None - if item_data.product_id is not None: - product_result = await db_session.execute( - select(Product).where(Product.id == item_data.product_id, Product.is_active == True) - ) - product = product_result.scalar_one_or_none() - if not product: - raise HTTPException(status_code=400, detail=f"产品不存在: {item_data.product_id}") - else: - if not item_data.product_sku or not item_data.product_name: - raise HTTPException(status_code=400, detail="请提供产品ID,或提供产品SKU与产品名称") - by_sku_result = await db_session.execute( - select(Product).where(Product.sku == item_data.product_sku, Product.is_active == True) - ) - product = by_sku_result.scalar_one_or_none() - if not product: - product = Product( - sku=item_data.product_sku, - name=item_data.product_name, - category=item_data.product_category, - unit=item_data.product_unit or "件", - item_type="finished", - cost_price=0, - sale_price=item_data.unit_price or 0, - min_stock=0, - max_stock=0 - ) - db_session.add(product) - await db_session.flush() - if product.item_type != "finished": - raise HTTPException(status_code=400, detail=f"销售单仅允许成品: {product.name}") - item = SalesOrderItem( - order_id=order.id, - product_id=product.id, - quantity=item_data.quantity, - unit_price=item_data.unit_price, - amount=item_data.quantity * item_data.unit_price, - remark=item_data.remark - ) - db_session.add(item) - total_amount += item.amount - return total_amount @router.get("", response_model=PaginatedResponse[SalesOrderResponse]) @@ -411,26 +34,7 @@ async def list_sales_orders( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - base_query = ( - select(SalesOrder, Customer) - .join(Customer, SalesOrder.customer_id == Customer.id) - .order_by(SalesOrder.created_at.desc()) - ) - - if status: - base_query = base_query.where(SalesOrder.status == status) - - count_query = select(func.count()).select_from(base_query.subquery()) - total = await db_session.scalar(count_query) or 0 - - query = base_query.offset(skip).limit(limit) - result = await db_session.execute(query) - - orders = [] - for order, customer in result.all(): - orders.append(_build_sales_order_response(order, customer.name)) - - return PaginatedResponse(items=orders, total=total, skip=skip, limit=limit) + return await sales_order_service.list_orders(db_session, status, skip, limit) @router.post("", response_model=SalesOrderResponse, status_code=201) @@ -439,35 +43,7 @@ async def create_sales_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - from datetime import datetime - now = datetime.now() - order = SalesOrder( - order_no=generate_order_no("SO"), - customer_id=order_data.customer_id, - order_date=now, - delivery_date=order_data.delivery_date, - manufacturing_date=now.date(), - created_at=now, - remark=order_data.remark, - operator_id=current_user.id, - status="manufacturing" - ) - db_session.add(order) - await db_session.flush() - - try: - order.total_amount = await _apply_order_items(db_session, order, order_data) - await _issue_materials_for_order_creation(db_session, order, current_user) - await db_session.commit() - except (HTTPException, Exception): - await db_session.rollback() - raise - await db_session.refresh(order) - - customer = await db_session.execute(select(Customer).where(Customer.id == order.customer_id)) - customer = customer.scalar_one() - - return _build_sales_order_response(order, customer.name) + return await sales_order_service.create_order(db_session, order_data, current_user) @router.get("/{order_id}", response_model=SalesOrderDetailResponse) @@ -476,8 +52,7 @@ async def get_sales_order_detail( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, customer = await _get_sales_order_with_customer(db_session, order_id) - return await _build_sales_order_detail_response(db_session, order, customer.name) + return await sales_order_service.get_detail(db_session, order_id) @router.put("/{order_id}", response_model=SalesOrderResponse) @@ -487,34 +62,7 @@ async def update_sales_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, customer = await _get_sales_order_with_customer(db_session, order_id) - if order.status == "paid": - raise HTTPException(status_code=400, detail="已收款的销售订单禁止修改") - try: - await _rollback_issued_materials(db_session, order, current_user) - await db_session.execute(delete(SalesOrderItem).where(SalesOrderItem.order_id == order.id)) - - order.customer_id = order_data.customer_id - order.delivery_date = order_data.delivery_date - order.remark = order_data.remark - order.production_status = "not_started" - order.production_no = None - order.planned_material_cost = 0 - order.actual_material_cost = 0 - order.status = "manufacturing" - - order.total_amount = await _apply_order_items(db_session, order, order_data) - await _issue_materials_for_order_creation(db_session, order, current_user) - await db_session.commit() - except (HTTPException, Exception): - await db_session.rollback() - raise - await db_session.refresh(order) - - customer_result = await db_session.execute(select(Customer).where(Customer.id == order.customer_id)) - updated_customer = customer_result.scalar_one_or_none() - customer_name = updated_customer.name if updated_customer else "未知客户" - return _build_sales_order_response(order, customer_name) + return await sales_order_service.update_order(db_session, order_id, order_data, current_user) @router.patch("/{order_id}/status", response_model=SalesOrderResponse) @@ -524,34 +72,7 @@ async def update_sales_order_status( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - if payload.status not in VALID_ORDER_STATUSES: - raise HTTPException(status_code=400, detail="订单状态必须为 manufacturing、delivered、paid") - - order, customer = await _get_sales_order_with_customer(db_session, order_id) - - # 状态转换守卫 - if order.status == "paid": - raise HTTPException(status_code=400, detail="已收款的销售订单禁止修改状态") - if order.status == "cancelled": - raise HTTPException(status_code=400, detail="已作废的销售订单禁止修改状态") - if order.status == "delivered" and payload.status not in ("paid", "cancelled"): - raise HTTPException(status_code=400, detail="已交付的销售订单只能修改为已收款或已作废") - if payload.status == "cancelled" and order.status == "paid": - raise HTTPException(status_code=400, detail="已收款的订单不能作废,请联系管理员") - - # 根据状态更新相应的日期字段 - from datetime import datetime - if payload.status == "delivered" and not order.actual_delivery_date: - order.actual_delivery_date = datetime.now() - elif payload.status == "paid" and not order.actual_payment_date: - order.actual_payment_date = datetime.now() - elif payload.status == "cancelled" and not order.actual_delivery_date: - order.actual_delivery_date = datetime.now() - - order.status = payload.status - await db_session.commit() - await db_session.refresh(order) - return _build_sales_order_response(order, customer.name) + return await sales_order_service.update_status(db_session, order_id, payload, current_user) @router.delete("/{order_id}") @@ -560,27 +81,7 @@ async def delete_sales_order( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, customer = await _get_sales_order_with_customer(db_session, order_id) - if order.status == "delivered": - raise HTTPException(status_code=400, detail="已交付的销售订单禁止删除") - if order.status == "paid": - raise HTTPException(status_code=400, detail="已收款的销售订单禁止删除,请先作废相关收款单") - if (order.received_amount or 0) > 0: - raise HTTPException(status_code=400, detail="该销售订单已存在收款记录(received_amount>0),禁止删除,请先作废相关收款单") - await _rollback_issued_materials(db_session, order, current_user) - await db_session.delete(order) - await db_session.commit() - return {"message": "销售订单已删除"} - - -class MaterialConsumptionItem(BaseModel): - material_id: int - quantity: float = Field(gt=0) - remark: Optional[str] = None - - -class MaterialConsumptionRequest(BaseModel): - items: List[MaterialConsumptionItem] = Field(min_length=1) + return await sales_order_service.delete_order(db_session, order_id, current_user) @router.post("/{order_id}/consume-materials") @@ -591,76 +92,7 @@ async def consume_materials( current_user: User = Depends(get_current_active_user) ): """记录销售订单的物料消耗(在已发料基础上记录额外消耗,如报废/超耗)""" - order, customer = await _get_sales_order_with_customer(db_session, order_id) - - if order.status in ("delivered", "paid", "cancelled"): - raise HTTPException(status_code=400, detail="当前订单状态不允许记录物料消耗") - if order.production_status == "completed": - raise HTTPException(status_code=400, detail="该销售单已完成生产,不允许记录物料消耗") - - default_warehouse = await _get_default_warehouse(db_session) - - # 计算总物料成本 - total_cost = Decimal("0") - - # 处理每个物料消耗项 - for item in request.items: - # 获取物料信息 - material = await db_session.get(Product, item.material_id) - if not material: - raise HTTPException(status_code=404, detail=f"物料 ID {item.material_id} 不存在") - if material.item_type != "material": - raise HTTPException(status_code=400, detail=f"只能消耗物料类型的产品: {material.name}") - - # 统一转 Decimal,避免 Decimal * float 在 Postgres(Numeric) 上抛 TypeError - consume_qty = Decimal(str(item.quantity)) - unit_cost = Decimal(str(material.cost_price or 0)) - cost = unit_cost * consume_qty - total_cost += cost - - # 更新物料库存(原子操作防并发) - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == material.id) - .where(Inventory.warehouse_id == default_warehouse.id) - .where(Inventory.quantity >= consume_qty) - .values(quantity=Inventory.quantity - consume_qty) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - raise HTTPException(status_code=400, detail=f"物料 {material.name} 库存不足") - after_qty = Decimal(str(after_qty)) - before_qty = after_qty + consume_qty - - # 记录物料消耗 - movement = StockMovement( - product_id=material.id, - warehouse_id=default_warehouse.id, - quantity=consume_qty, - before_quantity=before_qty, - after_quantity=after_qty, - movement_type="consumption", - reference_type="sales_order", - reference_id=order.id, - unit_price=unit_cost, - total_amount=cost, - operator_id=current_user.id, - remark=item.remark - ) - db_session.add(movement) - - # 累加实际物料成本(创建时已记录计划发料成本,此处为额外消耗,不应覆盖) - order.actual_material_cost = Decimal(str(order.actual_material_cost or 0)) + total_cost - - await db_session.commit() - await db_session.refresh(order) - - return { - "message": "物料消耗记录保存成功", - "total_cost": total_cost, - "order": _build_sales_order_response(order, customer.name) - } + return await sales_order_service.consume_materials(db_session, order_id, request, current_user) @router.get("/{order_id}/production-plan", response_model=SalesOrderProductionPlanResponse) @@ -669,18 +101,7 @@ async def get_sales_order_production_plan( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, customer = await _get_sales_order_with_customer(db_session, order_id) - production_no = order.production_no or generate_order_no("WO") - plan_items, planned_material_cost = await _build_material_plan(db_session, order) - - return SalesOrderProductionPlanResponse( - sales_order_id=order.id, - order_no=order.order_no, - customer_name=customer.name, - production_no=production_no, - planned_material_cost=planned_material_cost, - items=plan_items, - ) + return await sales_order_service.get_production_plan(db_session, order_id) @router.post("/{order_id}/issue-materials", response_model=SalesOrderIssueResponse) @@ -690,88 +111,4 @@ async def issue_sales_order_materials( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - order, _ = await _get_sales_order_with_customer(db_session, order_id) - if order.status == "delivered": - raise HTTPException(status_code=400, detail="已交付的销售订单禁止领料") - if order.production_status == "completed": - raise HTTPException(status_code=400, detail="该销售单已完成生产") - if order.production_status == "material_issued": - raise HTTPException(status_code=400, detail="该销售单已自动扣减过物料") - - warehouse_result = await db_session.execute( - select(Warehouse).where(Warehouse.id == payload.warehouse_id, Warehouse.is_active == True) - ) - warehouse = warehouse_result.scalar_one_or_none() - if not warehouse: - raise HTTPException(status_code=404, detail="仓库不存在") - - plan_items, planned_material_cost = await _build_material_plan(db_session, order) - if not plan_items: - raise HTTPException(status_code=400, detail="该销售单未配置BOM,无法领料") - - shortage_items = [item for item in plan_items if item.shortage_quantity > 0] - if shortage_items: - shortage_text = ",".join([f"{item.material_name} 缺 {item.shortage_quantity}" for item in shortage_items]) - raise HTTPException(status_code=400, detail=f"物料库存不足:{shortage_text}") - - production_no = payload.production_no or order.production_no or generate_order_no("WO") - - actual_material_cost = Decimal("0") - movement_count = 0 - for item in plan_items: - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == item.material_id) - .where(Inventory.warehouse_id == warehouse.id) - .where(Inventory.quantity >= item.required_quantity) - .values(quantity=Inventory.quantity - item.required_quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - raise HTTPException(status_code=400, detail=f"{item.material_name} 在所选仓库库存不足") - after_qty = int(after_qty) - before_qty = after_qty + int(item.required_quantity) - total_amount = item.required_quantity * item.unit_cost - actual_material_cost += total_amount - - movement = StockMovement( - product_id=item.material_id, - warehouse_id=warehouse.id, - movement_type="issue_to_production", - quantity=item.required_quantity, - before_quantity=before_qty, - after_quantity=after_qty, - reference_type="sales_order", - reference_id=order.id, - reference_no=production_no, - unit_price=item.unit_cost, - total_amount=Decimal(str(total_amount)), - remark=payload.remark or f"销售单{order.order_no}按单生产领料", - operator_id=current_user.id - ) - db_session.add(movement) - movement_count += 1 - - order.production_no = production_no - order.production_status = "material_issued" - order.planned_material_cost = planned_material_cost - order.actual_material_cost = actual_material_cost - if order.status == "draft": - order.status = "manufacturing" - - await db_session.commit() - - cost_deviation = actual_material_cost - planned_material_cost - cost_deviation_rate = (cost_deviation / planned_material_cost) if planned_material_cost > Decimal("1e-9") else Decimal("0") - return SalesOrderIssueResponse( - sales_order_id=order.id, - order_no=order.order_no, - production_no=production_no, - movement_count=movement_count, - planned_material_cost=planned_material_cost, - actual_material_cost=actual_material_cost, - cost_deviation=cost_deviation, - cost_deviation_rate=cost_deviation_rate, - production_status=order.production_status, - ) + return await sales_order_service.issue_materials(db_session, order_id, payload, current_user) diff --git a/src/inventory/api/stock_movement_routes.py b/src/inventory/api/stock_movement_routes.py index 7fa0f36..1f584e9 100644 --- a/src/inventory/api/stock_movement_routes.py +++ b/src/inventory/api/stock_movement_routes.py @@ -1,36 +1,20 @@ -""" -库存变动路由模块 - -提供库存变动的管理功能,包括: -- 创建库存变动记录(入库、出库、调整) -- 库存变动历史查询(支持分页、产品筛选、变动类型筛选) -- 自动更新库存数量 -- 库存不足校验(出库时) +"""库存变动路由层 - 薄路由 +业务逻辑下沉至 inventory.services.stock_movement_service,路由只做参数校验与响应组装。 路由前缀: /api/stock-movements """ -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy import select, update, func -from typing import Optional, List +from typing import Optional from shared.database.database import get_db_session from shared.services.auth_service import get_current_active_user -from shared.models.database import User, Product, Warehouse, Inventory, StockMovement +from shared.models.database import User from ..schemas import StockMovementCreate, StockMovementResponse, PaginatedResponse -from ..utils import generate_order_no +from ..services.stock_movement_service import stock_movement_service router = APIRouter(prefix="/stock-movements", tags=["库存变动"]) -INBOUND_TYPES = { - "in", "purchase_in", "return_from_production", "outsource_return", "finish_in" -} -OUTBOUND_TYPES = { - "out", "issue_to_production", "outsource_send", "shipment_out", "scrap_out" -} -ADJUST_TYPES = {"adjust"} -SUPPORTED_MOVEMENT_TYPES = INBOUND_TYPES | OUTBOUND_TYPES | ADJUST_TYPES - @router.post("", response_model=StockMovementResponse, status_code=201) async def create_stock_movement( @@ -38,130 +22,7 @@ async def create_stock_movement( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - if movement_data.movement_type not in SUPPORTED_MOVEMENT_TYPES: - raise HTTPException(status_code=400, detail="无效的变动类型") - if movement_data.quantity <= 0: - raise HTTPException(status_code=400, detail="数量必须大于0") - - warehouse_result = await db_session.execute( - select(Warehouse) - .where(Warehouse.id == movement_data.warehouse_id) - .where(Warehouse.is_active == True) - ) - warehouse = warehouse_result.scalar_one_or_none() - if not warehouse: - raise HTTPException(status_code=404, detail="仓库不存在") - - product_result = await db_session.execute( - select(Product) - .where(Product.id == movement_data.product_id) - .where(Product.is_active == True) - ) - product = product_result.scalar_one_or_none() - if not product: - sku_candidate = movement_data.product_sku or str(movement_data.product_id) - product_by_sku_result = await db_session.execute( - select(Product) - .where(Product.sku == sku_candidate) - .where(Product.is_active == True) - ) - product = product_by_sku_result.scalar_one_or_none() - if not product: - raise HTTPException(status_code=404, detail="产品不存在,请选择系统中的产品") - if product.item_type != "material": - raise HTTPException(status_code=400, detail="库存仅管理物料,该条目不是物料") - - resolved_product_id = product.id - - if movement_data.movement_type in INBOUND_TYPES: - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == resolved_product_id) - .where(Inventory.warehouse_id == movement_data.warehouse_id) - .values(quantity=Inventory.quantity + movement_data.quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - inventory = Inventory( - product_id=resolved_product_id, - warehouse_id=movement_data.warehouse_id, - quantity=movement_data.quantity, - ) - db_session.add(inventory) - await db_session.flush() - before_qty = 0 - after_qty = movement_data.quantity - else: - after_qty = int(after_qty) - before_qty = after_qty - movement_data.quantity - - elif movement_data.movement_type in OUTBOUND_TYPES: - upd_result = await db_session.execute( - update(Inventory) - .where(Inventory.product_id == resolved_product_id) - .where(Inventory.warehouse_id == movement_data.warehouse_id) - .where(Inventory.quantity >= movement_data.quantity) - .values(quantity=Inventory.quantity - movement_data.quantity) - .returning(Inventory.quantity) - ) - after_qty = upd_result.scalar_one_or_none() - if after_qty is None: - raise HTTPException(status_code=400, detail="库存不足") - after_qty = int(after_qty) - before_qty = after_qty + movement_data.quantity - - else: - result = await db_session.execute( - select(Inventory) - .where(Inventory.product_id == resolved_product_id) - .where(Inventory.warehouse_id == movement_data.warehouse_id) - .with_for_update() - ) - inventory = result.scalar_one_or_none() - if not inventory: - inventory = Inventory( - product_id=resolved_product_id, - warehouse_id=movement_data.warehouse_id, - quantity=0, - ) - db_session.add(inventory) - await db_session.flush() - before_qty = 0 - else: - before_qty = int(inventory.quantity) - inventory.quantity = movement_data.quantity - after_qty = movement_data.quantity - - movement = StockMovement( - product_id=resolved_product_id, - warehouse_id=movement_data.warehouse_id, - movement_type=movement_data.movement_type, - quantity=movement_data.quantity, - before_quantity=before_qty, - after_quantity=after_qty, - reference_no=generate_order_no("SM"), - unit_price=movement_data.unit_price, - total_amount=movement_data.unit_price * movement_data.quantity if movement_data.unit_price else None, - remark=movement_data.remark, - operator_id=current_user.id - ) - db_session.add(movement) - await db_session.commit() - - return StockMovementResponse( - id=movement.id, - product_id=product.id, - product_sku=product.sku, - product_name=product.name, - movement_type=movement.movement_type, - quantity=movement.quantity, - before_quantity=movement.before_quantity, - after_quantity=movement.after_quantity, - reference_no=movement.reference_no, - remark=movement.remark, - created_at=movement.created_at - ) + return await stock_movement_service.create_movement(db_session, movement_data, current_user) @router.get("", response_model=PaginatedResponse[StockMovementResponse]) @@ -173,37 +34,4 @@ async def list_stock_movements( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - base_query = ( - select(StockMovement, Product) - .join(Product, StockMovement.product_id == Product.id) - .order_by(StockMovement.created_at.desc()) - ) - - if product_id: - base_query = base_query.where(StockMovement.product_id == product_id) - if movement_type: - base_query = base_query.where(StockMovement.movement_type == movement_type) - - count_query = select(func.count()).select_from(base_query.subquery()) - total = await db_session.scalar(count_query) or 0 - - query = base_query.offset(skip).limit(limit) - result = await db_session.execute(query) - - movements = [] - for movement, product in result.all(): - movements.append(StockMovementResponse( - id=movement.id, - product_id=product.id, - product_sku=product.sku, - product_name=product.name, - movement_type=movement.movement_type, - quantity=movement.quantity, - before_quantity=movement.before_quantity, - after_quantity=movement.after_quantity, - reference_no=movement.reference_no, - remark=movement.remark, - created_at=movement.created_at - )) - - return PaginatedResponse(items=movements, total=total, skip=skip, limit=limit) + return await stock_movement_service.list_movements(db_session, product_id, movement_type, skip, limit) diff --git a/src/inventory/api/utils.py b/src/inventory/api/utils.py deleted file mode 100644 index a451f7a..0000000 --- a/src/inventory/api/utils.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -库存管理工具函数模块 - -提供进销存系统通用的工具函数,包括: -- 订单编号生成器(采购订单、销售订单、库存变动等) -""" -from datetime import datetime, timezone -import secrets - - -def generate_order_no(prefix: str) -> str: - """生成订单编号 - - Args: - prefix: 订单类型前缀,如 PO(采购订单)、SO(销售订单)、SM(库存变动) - - Returns: - 格式为 {prefix}{YYYYMMDDHHMMSS}{8位随机字符} 的订单编号 - """ - date_str = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S") - random_str = secrets.token_hex(4).upper() - return f"{prefix}{date_str}{random_str}" diff --git a/src/inventory/schemas/__init__.py b/src/inventory/schemas/__init__.py index d54ae74..c3f1515 100644 --- a/src/inventory/schemas/__init__.py +++ b/src/inventory/schemas/__init__.py @@ -30,7 +30,9 @@ from .sales_order_schemas import ( SalesOrderProductionPlanResponse, SalesOrderIssueRequest, SalesOrderIssueResponse, - SalesOrderStatusUpdate + SalesOrderStatusUpdate, + MaterialConsumptionItem, + MaterialConsumptionRequest ) from .finance_schemas import ( FinanceAllocationCreate, diff --git a/src/inventory/schemas/sales_order_schemas.py b/src/inventory/schemas/sales_order_schemas.py index 8ea0775..ff5441a 100644 --- a/src/inventory/schemas/sales_order_schemas.py +++ b/src/inventory/schemas/sales_order_schemas.py @@ -102,3 +102,13 @@ class SalesOrderIssueResponse(BaseModel): class SalesOrderStatusUpdate(BaseModel): status: str + + +class MaterialConsumptionItem(BaseModel): + material_id: int + quantity: float = Field(gt=0) + remark: Optional[str] = None + + +class MaterialConsumptionRequest(BaseModel): + items: List[MaterialConsumptionItem] = Field(min_length=1) diff --git a/src/inventory/services/inventory_service.py b/src/inventory/services/inventory_service.py new file mode 100644 index 0000000..aa52e9c --- /dev/null +++ b/src/inventory/services/inventory_service.py @@ -0,0 +1,180 @@ +"""库存查询业务服务层 + +将原 inventory_routes 中的业务编排(库存列表/建/改/删)下沉到此, +路由层只做参数校验与响应组装。 +""" +from typing import Optional + +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, func +from sqlalchemy.exc import IntegrityError + +from shared.models.database import User, Product, Warehouse, Inventory +from ..schemas import InventoryResponse, InventoryCreate, InventoryUpdate, PaginatedResponse + + +def _build_inventory_response(inv: Inventory, product: Product, warehouse: Warehouse) -> InventoryResponse: + return InventoryResponse( + id=inv.id, + product_id=inv.product_id, + product_name=product.name, + product_sku=product.sku, + warehouse_id=inv.warehouse_id, + warehouse_name=warehouse.name, + quantity=inv.quantity, + locked_quantity=inv.locked_quantity, + available_quantity=inv.available_quantity + ) + + +class InventoryService: + """库存查询业务服务""" + + @staticmethod + async def list_inventory( + db_session: AsyncSession, + warehouse_id: Optional[int], + product_id: Optional[int], + low_stock: bool, + skip: int, + limit: int, + ) -> PaginatedResponse: + base_query = ( + select(Inventory, Product, Warehouse) + .join(Product, Inventory.product_id == Product.id) + .join(Warehouse, Inventory.warehouse_id == Warehouse.id) + .where(Product.is_active == True) + .where(Product.item_type == "material") + .where(Warehouse.is_active == True) + ) + + if warehouse_id: + base_query = base_query.where(Inventory.warehouse_id == warehouse_id) + if product_id: + base_query = base_query.where(Inventory.product_id == product_id) + if low_stock: + base_query = base_query.where(Inventory.quantity <= Product.min_stock) + + count_query = select(func.count()).select_from(base_query.subquery()) + total = await db_session.scalar(count_query) or 0 + + query = base_query.offset(skip).limit(limit) + result = await db_session.execute(query) + + inventory_list = [_build_inventory_response(inv, product, warehouse) for inv, product, warehouse in result.all()] + return PaginatedResponse(items=inventory_list, total=total, skip=skip, limit=limit) + + @staticmethod + async def create_inventory( + db_session: AsyncSession, + payload: InventoryCreate, + current_user: User, + ) -> InventoryResponse: + if payload.quantity < 0 or payload.locked_quantity < 0: + raise HTTPException(status_code=400, detail="库存数量不能为负数") + if payload.locked_quantity > payload.quantity: + raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") + + product_result = await db_session.execute( + select(Product).where(Product.id == payload.product_id, Product.is_active == True) + ) + product = product_result.scalar_one_or_none() + if not product: + raise HTTPException(status_code=404, detail="物料不存在") + if product.item_type != "material": + raise HTTPException(status_code=400, detail="库存仅支持物料") + + warehouse_result = await db_session.execute( + select(Warehouse).where(Warehouse.id == payload.warehouse_id, Warehouse.is_active == True) + ) + warehouse = warehouse_result.scalar_one_or_none() + if not warehouse: + raise HTTPException(status_code=404, detail="仓库不存在") + + exists_result = await db_session.execute( + select(Inventory).where( + Inventory.product_id == payload.product_id, + Inventory.warehouse_id == payload.warehouse_id + ) + ) + if exists_result.scalar_one_or_none(): + raise HTTPException(status_code=400, detail="该仓库已存在该物料库存记录") + + inventory = Inventory( + product_id=payload.product_id, + warehouse_id=payload.warehouse_id, + quantity=payload.quantity, + locked_quantity=payload.locked_quantity, + batch_number=payload.batch_number, + location=payload.location + ) + db_session.add(inventory) + try: + await db_session.commit() + except IntegrityError: + # 并发创建命中 (product_id, warehouse_id) 唯一约束 + await db_session.rollback() + raise HTTPException(status_code=400, detail="该仓库已存在该物料库存记录") + await db_session.refresh(inventory) + + return _build_inventory_response(inventory, product, warehouse) + + @staticmethod + async def update_inventory( + db_session: AsyncSession, + inventory_id: int, + payload: InventoryUpdate, + current_user: User, + ) -> InventoryResponse: + result = await db_session.execute( + select(Inventory, Product, Warehouse) + .join(Product, Inventory.product_id == Product.id) + .join(Warehouse, Inventory.warehouse_id == Warehouse.id) + .where(Inventory.id == inventory_id) + .where(Product.item_type == "material") + .with_for_update(of=Inventory) + ) + row = result.first() + if not row: + raise HTTPException(status_code=404, detail="库存记录不存在") + inventory, product, warehouse = row + + if payload.quantity is not None: + if payload.quantity < 0: + raise HTTPException(status_code=400, detail="库存数量不能为负数") + inventory.quantity = payload.quantity + if payload.locked_quantity is not None: + if payload.locked_quantity < 0: + raise HTTPException(status_code=400, detail="锁定数量不能为负数") + inventory.locked_quantity = payload.locked_quantity + if inventory.locked_quantity > inventory.quantity: + raise HTTPException(status_code=400, detail="锁定数量不能大于库存数量") + + if payload.batch_number is not None: + inventory.batch_number = payload.batch_number + if payload.location is not None: + inventory.location = payload.location + + await db_session.commit() + await db_session.refresh(inventory) + return _build_inventory_response(inventory, product, warehouse) + + @staticmethod + async def delete_inventory( + db_session: AsyncSession, + inventory_id: int, + current_user: User, + ) -> dict: + result = await db_session.execute(select(Inventory).where(Inventory.id == inventory_id)) + inventory = result.scalar_one_or_none() + if not inventory: + raise HTTPException(status_code=404, detail="库存记录不存在") + if inventory.quantity > 0: + raise HTTPException(status_code=400, detail="库存数量不为零,无法删除库存记录") + await db_session.delete(inventory) + await db_session.commit() + return {"message": "库存记录已删除"} + + +inventory_service = InventoryService() diff --git a/src/inventory/services/purchase_order_service.py b/src/inventory/services/purchase_order_service.py new file mode 100644 index 0000000..abf6954 --- /dev/null +++ b/src/inventory/services/purchase_order_service.py @@ -0,0 +1,454 @@ +"""采购订单业务服务层 + +将原 purchase_order_routes 中的业务编排(建单/改单/删单、收货入库、状态流转)下沉到此, +路由层只做参数校验与响应组装。 +""" +from typing import Optional, Tuple +from decimal import Decimal + +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, func, update + +from shared.models.database import ( + User, + Supplier, + Product, + Warehouse, + Inventory, + StockMovement, + PurchaseOrder, + PurchaseOrderItem, +) +from ..schemas import ( + PurchaseOrderCreate, + PurchaseOrderResponse, + PurchaseOrderDetailResponse, + PurchaseOrderItemResponse, + PurchaseOrderReceiveRequest, + PaginatedResponse, +) +from ..utils import generate_order_no + + +def _compute_receipt_status(order: PurchaseOrder) -> str: + """收货状态:已下单 / 部分收货 / 已收货 / 已作废""" + if order.status == "cancelled": + return "cancelled" + if order.status in ("received", "paid"): + return "received" + if order.status == "partial_received": + return "partial_received" + return "pending" + + +def _compute_payment_status(order: PurchaseOrder) -> str: + """付款状态:未付款 / 已付款""" + if order.status == "paid" or order.paid_date: + return "paid" + return "unpaid" + + +def _build_purchase_order_response(order: PurchaseOrder, supplier_name: str) -> PurchaseOrderResponse: + return PurchaseOrderResponse( + id=order.id, + order_no=order.order_no, + supplier_name=supplier_name, + order_date=order.order_date, + expected_date=order.expected_date, + status=order.status, + receipt_status=_compute_receipt_status(order), + payment_status=_compute_payment_status(order), + total_amount=order.total_amount, + paid_amount=order.paid_amount, + remark=order.remark, + created_at=order.created_at, + received_date=order.received_date, + paid_date=order.paid_date + ) + + +async def _get_order_with_supplier( + db_session: AsyncSession, + order_id: int, +) -> Tuple[PurchaseOrder, Supplier]: + result = await db_session.execute( + select(PurchaseOrder, Supplier) + .join(Supplier, PurchaseOrder.supplier_id == Supplier.id) + .where(PurchaseOrder.id == order_id) + ) + row = result.first() + if not row: + raise HTTPException(status_code=404, detail="采购订单不存在") + return row[0], row[1] + + +async def _build_purchase_order_detail( + db_session: AsyncSession, + order: PurchaseOrder, + supplier_name: str, +) -> PurchaseOrderDetailResponse: + item_result = await db_session.execute( + select(PurchaseOrderItem, Product) + .join(Product, PurchaseOrderItem.product_id == Product.id) + .where(PurchaseOrderItem.order_id == order.id) + .order_by(PurchaseOrderItem.id.asc()) + ) + item_rows = item_result.all() + + return PurchaseOrderDetailResponse( + id=order.id, + order_no=order.order_no, + supplier_id=order.supplier_id, + supplier_name=supplier_name, + order_date=order.order_date, + expected_date=order.expected_date, + status=order.status, + receipt_status=_compute_receipt_status(order), + payment_status=_compute_payment_status(order), + total_amount=order.total_amount, + paid_amount=order.paid_amount, + remark=order.remark, + created_at=order.created_at, + received_date=order.received_date, + paid_date=order.paid_date, + items=[ + PurchaseOrderItemResponse( + id=item.id, + product_id=item.product_id, + product_sku=product.sku, + product_name=product.name, + quantity=item.quantity, + received_quantity=item.received_quantity, + unit_price=item.unit_price, + amount=item.amount, + remark=item.remark + ) for item, product in item_rows + ] + ) + + +async def _resolve_receive_warehouse( + db_session: AsyncSession, + warehouse_id: Optional[int], +) -> Warehouse: + if warehouse_id: + result = await db_session.execute( + select(Warehouse).where(Warehouse.id == warehouse_id, Warehouse.is_active == True) + ) + warehouse = result.scalar_one_or_none() + if not warehouse: + raise HTTPException(status_code=404, detail="仓库不存在") + return warehouse + + result = await db_session.execute( + select(Warehouse).where(Warehouse.is_active == True).order_by(Warehouse.is_default.desc(), Warehouse.id.asc()) + ) + warehouse = result.scalars().first() + if not warehouse: + raise HTTPException(status_code=400, detail="未配置可用仓库") + return warehouse + + +async def _apply_order_items( + db_session: AsyncSession, + order: PurchaseOrder, + order_data: PurchaseOrderCreate, +) -> Decimal: + total_amount = Decimal("0") + for item_data in order_data.items: + product_result = await db_session.execute( + select(Product).where(Product.id == item_data.product_id, Product.is_active == True) + ) + product = product_result.scalar_one_or_none() + if not product: + raise HTTPException(status_code=400, detail=f"物料不存在: {item_data.product_id}") + if product.item_type != "material": + raise HTTPException(status_code=400, detail=f"采购单仅允许物料: {product.name}") + + # 优先使用前端传入的单价,否则使用物料成本价 + if item_data.unit_price and item_data.unit_price > 0: + unit_price = float(item_data.unit_price) + else: + unit_price = product.cost_price or 0 + if unit_price <= 0: + raise HTTPException(status_code=400, detail=f"物料 {product.name} 未设置价格,请在物料管理界面设置成本价或在采购明细中填写单价") + + try: + item = PurchaseOrderItem( + order_id=order.id, + product_id=item_data.product_id, + quantity=int(item_data.quantity), + unit_price=Decimal(str(unit_price)), + amount=Decimal(str(item_data.quantity)) * Decimal(str(unit_price)), + remark=item_data.remark or None + ) + db_session.add(item) + total_amount += item.amount + except Exception as e: + raise HTTPException(status_code=400, detail=f"创建订单明细失败: {str(e)}") + return total_amount + + +class PurchaseOrderService: + """采购订单业务服务""" + + @staticmethod + async def list_orders( + db_session: AsyncSession, + status: Optional[str], + skip: int, + limit: int, + ) -> PaginatedResponse: + base_query = ( + select(PurchaseOrder, Supplier) + .join(Supplier, PurchaseOrder.supplier_id == Supplier.id) + .order_by(PurchaseOrder.created_at.desc()) + ) + + if status: + base_query = base_query.where(PurchaseOrder.status == status) + + count_query = select(func.count()).select_from(base_query.subquery()) + total = await db_session.scalar(count_query) or 0 + + query = base_query.offset(skip).limit(limit) + result = await db_session.execute(query) + + orders = [] + for order, supplier in result.all(): + orders.append(_build_purchase_order_response(order, supplier.name)) + + return PaginatedResponse(items=orders, total=total, skip=skip, limit=limit) + + @staticmethod + async def create_order( + db_session: AsyncSession, + order_data: PurchaseOrderCreate, + current_user: User, + ) -> PurchaseOrderResponse: + order = PurchaseOrder( + order_no=generate_order_no("PO"), + supplier_id=order_data.supplier_id, + expected_date=order_data.expected_date, + remark=order_data.remark, + operator_id=current_user.id, + status="pending" + ) + db_session.add(order) + await db_session.flush() + + try: + order.total_amount = await _apply_order_items(db_session, order, order_data) + await db_session.commit() + except (HTTPException, Exception): + await db_session.rollback() + raise + await db_session.refresh(order) + + supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) + supplier = supplier_result.scalar_one_or_none() + if not supplier: + raise HTTPException(status_code=404, detail="供应商不存在") + + return _build_purchase_order_response(order, supplier.name) + + @staticmethod + async def get_detail(db_session: AsyncSession, order_id: int) -> PurchaseOrderDetailResponse: + order, supplier = await _get_order_with_supplier(db_session, order_id) + return await _build_purchase_order_detail(db_session, order, supplier.name) + + @staticmethod + async def update_order( + db_session: AsyncSession, + order_id: int, + order_data: PurchaseOrderCreate, + current_user: User, + ) -> PurchaseOrderResponse: + order, _ = await _get_order_with_supplier(db_session, order_id) + if order.paid_amount and order.paid_amount > 0: + raise HTTPException(status_code=400, detail="已付款采购单不允许修改") + + item_result = await db_session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + existing_items = item_result.scalars().all() + if any((item.received_quantity or 0) > 0 for item in existing_items): + raise HTTPException(status_code=400, detail="已发生入库的采购单不允许直接修改") + + for item in existing_items: + await db_session.delete(item) + + try: + order.supplier_id = order_data.supplier_id + order.expected_date = order_data.expected_date + order.remark = order_data.remark + order.total_amount = await _apply_order_items(db_session, order, order_data) + order.status = "pending" + + await db_session.commit() + except (HTTPException, Exception): + await db_session.rollback() + raise + await db_session.refresh(order) + supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) + supplier = supplier_result.scalar_one_or_none() + return _build_purchase_order_response(order, supplier.name if supplier else "未知供应商") + + @staticmethod + async def delete_order(db_session: AsyncSession, order_id: int, current_user: User) -> dict: + order, _ = await _get_order_with_supplier(db_session, order_id) + if order.paid_amount and order.paid_amount > 0: + raise HTTPException(status_code=400, detail="已付款采购单不允许删除") + item_result = await db_session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + if any((item.received_quantity or 0) > 0 for item in item_result.scalars().all()): + raise HTTPException(status_code=400, detail="已发生入库的采购单不允许删除") + await db_session.delete(order) + await db_session.commit() + return {"message": "采购订单已删除"} + + @staticmethod + async def update_status( + db_session: AsyncSession, + order_id: int, + status: dict, + current_user: User, + ) -> PurchaseOrderResponse: + order, _ = await _get_order_with_supplier(db_session, order_id) + + new_status = status.get("status") + if not new_status: + raise HTTPException(status_code=400, detail="状态不能为空") + + valid_statuses = ["pending", "partial_received", "received", "paid", "cancelled"] + if new_status not in valid_statuses: + raise HTTPException(status_code=400, detail=f"无效的状态值,有效值为: {valid_statuses}") + + # 收货状态(partial_received/received)只能由收货端点驱动,禁止手动设置,避免与实际入库脱钩 + if new_status in ("partial_received", "received"): + raise HTTPException(status_code=400, detail="收货状态只能通过收货入库操作自动变更,不能手动设置") + + # 状态转换逻辑 + if order.status == "paid": + raise HTTPException(status_code=400, detail="已付款的采购订单禁止修改状态") + if order.status == "cancelled": + raise HTTPException(status_code=400, detail="已作废的采购订单禁止修改状态") + if order.status == "received" and new_status not in ("paid", "cancelled"): + raise HTTPException(status_code=400, detail="已收货的采购订单只能标记为已付款或已作废") + if order.status == "partial_received" and new_status not in ("received", "paid", "cancelled"): + raise HTTPException(status_code=400, detail="部分收货的采购订单只能标记为已收货、已付款或已作废") + if new_status == "cancelled" and order.status == "paid": + raise HTTPException(status_code=400, detail="已付款的订单不能作废") + + # 更新状态和对应时间 + order.status = new_status + if new_status == "received": + order.received_date = func.now() + elif new_status == "paid": + order.paid_date = func.now() + + await db_session.commit() + await db_session.refresh(order) + + supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id)) + supplier = supplier_result.scalar_one_or_none() + return _build_purchase_order_response(order, supplier.name if supplier else "未知供应商") + + @staticmethod + async def receive_order( + db_session: AsyncSession, + order_id: int, + payload: PurchaseOrderReceiveRequest, + current_user: User, + ) -> PurchaseOrderDetailResponse: + order, supplier = await _get_order_with_supplier(db_session, order_id) + if order.status in ("cancelled", "paid"): + raise HTTPException(status_code=400, detail=f"当前采购单状态为 {order.status},不允许收货") + warehouse = await _resolve_receive_warehouse(db_session, payload.warehouse_id) + + item_result = await db_session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + item_map = {item.id: item for item in item_result.scalars().all()} + if not item_map: + raise HTTPException(status_code=400, detail="采购单无明细,无法入库") + + if not payload.items: + raise HTTPException(status_code=400, detail="请提供本次入库明细") + + for receive_item in payload.items: + item = item_map.get(receive_item.item_id) + if not item: + raise HTTPException(status_code=400, detail=f"采购明细不存在: {receive_item.item_id}") + if receive_item.receive_quantity <= 0: + raise HTTPException(status_code=400, detail="入库数量必须大于0") + remaining_qty = (item.quantity or 0) - (item.received_quantity or 0) + if receive_item.receive_quantity > remaining_qty: + raise HTTPException(status_code=400, detail=f"明细{item.id}入库超量,剩余可入库{remaining_qty}") + + for receive_item in payload.items: + item = item_map[receive_item.item_id] + product_result = await db_session.execute( + select(Product).where(Product.id == item.product_id) + ) + product = product_result.scalar_one_or_none() + if not product: + raise HTTPException(status_code=400, detail=f"物料不存在: {item.product_id}") + + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == item.product_id) + .where(Inventory.warehouse_id == warehouse.id) + .values(quantity=Inventory.quantity + receive_item.receive_quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + inventory = Inventory( + product_id=item.product_id, + warehouse_id=warehouse.id, + quantity=receive_item.receive_quantity, + locked_quantity=0 + ) + db_session.add(inventory) + await db_session.flush() + before_qty = 0 + after_qty = receive_item.receive_quantity + else: + after_qty = int(after_qty) + before_qty = after_qty - receive_item.receive_quantity + item.received_quantity = (item.received_quantity or 0) + receive_item.receive_quantity + + movement = StockMovement( + product_id=item.product_id, + warehouse_id=warehouse.id, + movement_type="purchase_in", + quantity=receive_item.receive_quantity, + before_quantity=before_qty, + after_quantity=after_qty, + reference_type="purchase_order", + reference_id=order.id, + reference_no=order.order_no, + unit_price=item.unit_price, + total_amount=Decimal(str(item.unit_price * receive_item.receive_quantity)), + remark=payload.remark or f"采购单{order.order_no}到货入库", + operator_id=current_user.id + ) + db_session.add(movement) + + all_received = all((item.received_quantity or 0) >= (item.quantity or 0) for item in item_map.values()) + any_received = any((item.received_quantity or 0) > 0 for item in item_map.values()) + if all_received: + order.status = "received" + order.received_date = func.now() + elif any_received: + order.status = "partial_received" + + await db_session.commit() + await db_session.refresh(order) + return await _build_purchase_order_detail(db_session, order, supplier.name) + + +purchase_order_service = PurchaseOrderService() diff --git a/src/inventory/services/sales_order_service.py b/src/inventory/services/sales_order_service.py new file mode 100644 index 0000000..db4b9bc --- /dev/null +++ b/src/inventory/services/sales_order_service.py @@ -0,0 +1,744 @@ +"""销售订单业务服务层 + +将原 sales_order_routes 中的业务编排(建单/改单/删单、BOM 物料计划、自动发料/回补、 +物料消耗、领料、状态流转)下沉到此,路由层只做参数校验与响应组装。 +""" +from datetime import datetime +from math import ceil +from typing import Optional, List, Tuple +from decimal import Decimal + +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, func, delete, update + +from shared.models.database import ( + User, + Customer, + Product, + ProductMaterial, + Warehouse, + Inventory, + StockMovement, + SalesOrder, + SalesOrderItem, +) +from ..schemas import ( + SalesOrderCreate, + SalesOrderResponse, + SalesOrderDetailResponse, + SalesOrderItemResponse, + SalesOrderProductionPlanResponse, + ProductionMaterialPlanItemResponse, + SalesOrderIssueRequest, + SalesOrderIssueResponse, + SalesOrderStatusUpdate, + MaterialConsumptionRequest, + PaginatedResponse, +) +from ..utils import generate_order_no + +VALID_ORDER_STATUSES = {"manufacturing", "delivered", "paid", "cancelled"} +PRODUCTION_STATUSES = {"not_started", "bom_missing", "material_issued", "completed"} + + +def _compute_delivery_status(order: SalesOrder) -> str: + """物流状态:制造中 / 已交付 / 已作废""" + if order.status == "cancelled": + return "cancelled" + if order.status in ("delivered", "paid") or order.actual_delivery_date: + return "delivered" + return "manufacturing" + + +def _compute_payment_status(order: SalesOrder) -> str: + """收款状态:未收款 / 已收款""" + if order.status == "paid" or order.actual_payment_date: + return "paid" + return "unpaid" + + +def _build_sales_order_response(order: SalesOrder, customer_name: str) -> SalesOrderResponse: + return SalesOrderResponse( + id=order.id, + order_no=order.order_no, + customer_name=customer_name, + order_date=order.order_date, + delivery_date=order.delivery_date, + manufacturing_date=order.manufacturing_date, + actual_delivery_date=order.actual_delivery_date, + actual_payment_date=order.actual_payment_date, + status=order.status, + delivery_status=_compute_delivery_status(order), + payment_status=_compute_payment_status(order), + production_status=order.production_status or "not_started", + production_no=order.production_no, + planned_material_cost=Decimal(str(order.planned_material_cost or 0)), + actual_material_cost=Decimal(str(order.actual_material_cost or 0)), + total_amount=Decimal(str(order.total_amount or 0)), + received_amount=Decimal(str(order.received_amount or 0)), + remark=order.remark, + created_at=order.created_at + ) + + +async def _build_sales_order_detail_response( + db_session: AsyncSession, + order: SalesOrder, + customer_name: str +) -> SalesOrderDetailResponse: + items_result = await db_session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order.id).order_by(SalesOrderItem.id.asc()) + ) + items = items_result.scalars().all() + return SalesOrderDetailResponse( + id=order.id, + order_no=order.order_no, + customer_id=order.customer_id, + customer_name=customer_name, + order_date=order.order_date, + delivery_date=order.delivery_date, + manufacturing_date=order.manufacturing_date, + actual_delivery_date=order.actual_delivery_date, + actual_payment_date=order.actual_payment_date, + status=order.status, + delivery_status=_compute_delivery_status(order), + payment_status=_compute_payment_status(order), + production_status=order.production_status or "not_started", + production_no=order.production_no, + planned_material_cost=Decimal(str(order.planned_material_cost or 0)), + actual_material_cost=Decimal(str(order.actual_material_cost or 0)), + total_amount=Decimal(str(order.total_amount or 0)), + received_amount=Decimal(str(order.received_amount or 0)), + remark=order.remark, + created_at=order.created_at, + items=[ + SalesOrderItemResponse( + id=item.id, + product_id=item.product_id, + quantity=item.quantity, + delivered_quantity=item.delivered_quantity, + unit_price=Decimal(str(item.unit_price or 0)), + amount=Decimal(str(item.amount or 0)), + remark=item.remark + ) for item in items + ] + ) + + +async def _get_sales_order_with_customer( + db_session: AsyncSession, + order_id: int, +) -> Tuple[SalesOrder, Customer]: + result = await db_session.execute( + select(SalesOrder, Customer) + .join(Customer, SalesOrder.customer_id == Customer.id) + .where(SalesOrder.id == order_id) + ) + row = result.first() + if not row: + raise HTTPException(status_code=404, detail="销售订单不存在") + return row[0], row[1] + + +async def _build_material_plan(db_session: AsyncSession, order: SalesOrder) -> Tuple[List[ProductionMaterialPlanItemResponse], Decimal]: + item_result = await db_session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order.id) + ) + order_items = item_result.scalars().all() + if not order_items: + return [], 0 + + finished_ids = list({int(i.product_id) for i in order_items}) + bom_result = await db_session.execute( + select(ProductMaterial, Product) + .join(Product, ProductMaterial.material_product_id == Product.id) + .where(ProductMaterial.finished_product_id.in_(finished_ids)) + .where(Product.is_active == True) + .where(Product.item_type == "material") + ) + bom_rows = bom_result.all() + if not bom_rows: + return [], 0 + + bom_by_finished_id = {} + for bom, material in bom_rows: + bom_by_finished_id.setdefault(int(bom.finished_product_id), []).append((bom, material)) + + required_qty_map = {} + for order_item in order_items: + bom_items = bom_by_finished_id.get(int(order_item.product_id)) or [] + for bom, material in bom_items: + qty = Decimal(str(order_item.quantity)) * Decimal(str(bom.quantity or 0)) * (1 + Decimal(str(bom.loss_rate or 0))) + entry = required_qty_map.setdefault(material.id, {"material": material, "required_qty": Decimal("0")}) + entry["required_qty"] += qty + + if not required_qty_map: + return [], Decimal("0") + + material_ids = list(required_qty_map.keys()) + stock_result = await db_session.execute( + select(Inventory.product_id, func.coalesce(func.sum(Inventory.quantity), 0)) + .where(Inventory.product_id.in_(material_ids)) + .group_by(Inventory.product_id) + ) + stock_map = {row[0]: Decimal(str(row[1] or 0)) for row in stock_result.all()} + + plan_items = [] + planned_material_cost = Decimal("0") + for material_id, entry in required_qty_map.items(): + material = entry["material"] + required_qty = int(ceil(entry["required_qty"])) + available_qty = stock_map.get(material_id, Decimal("0")) + shortage_qty = max(required_qty - int(available_qty), 0) + unit_cost = Decimal(str(material.cost_price or 0)) + required_cost = Decimal(str(required_qty)) * unit_cost + planned_material_cost += required_cost + plan_items.append( + ProductionMaterialPlanItemResponse( + material_id=material.id, + material_sku=material.sku, + material_name=material.name, + required_quantity=Decimal(str(required_qty)), + available_quantity=available_qty, + shortage_quantity=Decimal(str(shortage_qty)), + unit_cost=unit_cost, + required_cost=required_cost, + ) + ) + + plan_items = sorted(plan_items, key=lambda x: (x.shortage_quantity, x.required_cost), reverse=True) + return plan_items, planned_material_cost + + +async def _get_default_warehouse(db_session: AsyncSession) -> Warehouse: + warehouse_result = await db_session.execute( + select(Warehouse).where(Warehouse.is_active == True).order_by(Warehouse.is_default.desc(), Warehouse.id.asc()) + ) + warehouse = warehouse_result.scalars().first() + if not warehouse: + raise HTTPException(status_code=400, detail="未配置可用仓库,无法自动扣减物料") + return warehouse + + +async def _issue_materials_for_order_creation( + db_session: AsyncSession, + order: SalesOrder, + current_user: User +) -> Tuple[int, Decimal, Decimal]: + warehouse = await _get_default_warehouse(db_session) + plan_items, planned_material_cost = await _build_material_plan(db_session, order) + if not plan_items: + order.production_no = order.production_no or generate_order_no("WO") + order.production_status = "bom_missing" + order.planned_material_cost = 0 + order.actual_material_cost = 0 + order.status = "manufacturing" + return 0, 0, 0 + shortage_items = [item for item in plan_items if item.shortage_quantity > 0] + if shortage_items: + shortage_text = ",".join([f"{item.material_name} 缺 {item.shortage_quantity}" for item in shortage_items]) + raise HTTPException(status_code=400, detail=f"物料库存不足:{shortage_text}") + + production_no = order.production_no or generate_order_no("WO") + actual_material_cost = Decimal("0") + movement_count = 0 + + for item in plan_items: + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == item.material_id) + .where(Inventory.warehouse_id == warehouse.id) + .where(Inventory.quantity >= item.required_quantity) + .values(quantity=Inventory.quantity - item.required_quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + raise HTTPException(status_code=400, detail=f"{item.material_name} 在默认仓库库存不足") + after_qty = int(after_qty) + before_qty = after_qty + int(item.required_quantity) + total_amount = item.required_quantity * item.unit_cost + actual_material_cost += total_amount + + movement = StockMovement( + product_id=item.material_id, + warehouse_id=warehouse.id, + movement_type="issue_to_production", + quantity=item.required_quantity, + before_quantity=before_qty, + after_quantity=after_qty, + reference_type="sales_order", + reference_id=order.id, + reference_no=production_no, + unit_price=item.unit_cost, + total_amount=Decimal(str(total_amount)), + remark=f"销售单{order.order_no}创建时自动扣减物料", + operator_id=current_user.id + ) + db_session.add(movement) + movement_count += 1 + + order.production_no = production_no + order.production_status = "material_issued" + order.planned_material_cost = planned_material_cost + order.actual_material_cost = actual_material_cost + order.status = "manufacturing" + + return movement_count, planned_material_cost, actual_material_cost + + +async def _rollback_issued_materials( + db_session: AsyncSession, + order: SalesOrder, + current_user: User +): + movement_result = await db_session.execute( + select(StockMovement) + .where(StockMovement.reference_type == "sales_order") + .where(StockMovement.reference_id == order.id) + .where(StockMovement.movement_type == "issue_to_production") + .order_by(StockMovement.id.asc()) + ) + movements = movement_result.scalars().all() + if not movements: + return + + for movement in movements: + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == movement.product_id) + .where(Inventory.warehouse_id == movement.warehouse_id) + .values(quantity=Inventory.quantity + movement.quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + inventory = Inventory( + product_id=movement.product_id, + warehouse_id=movement.warehouse_id, + quantity=movement.quantity, + locked_quantity=0 + ) + db_session.add(inventory) + await db_session.flush() + before_qty = 0 + after_qty = movement.quantity + else: + after_qty = int(after_qty) + before_qty = after_qty - movement.quantity + + revert_movement = StockMovement( + product_id=movement.product_id, + warehouse_id=movement.warehouse_id, + movement_type="return_from_production", + quantity=movement.quantity, + before_quantity=before_qty, + after_quantity=after_qty, + reference_type="sales_order", + reference_id=order.id, + reference_no=order.production_no or order.order_no, + unit_price=movement.unit_price, + total_amount=movement.total_amount, + remark=f"销售单{order.order_no}变更/删除,自动回补物料", + operator_id=current_user.id + ) + db_session.add(revert_movement) + + +async def _apply_order_items( + db_session: AsyncSession, + order: SalesOrder, + order_data: SalesOrderCreate +) -> Decimal: + total_amount = Decimal("0") + for item_data in order_data.items: + product = None + if item_data.product_id is not None: + product_result = await db_session.execute( + select(Product).where(Product.id == item_data.product_id, Product.is_active == True) + ) + product = product_result.scalar_one_or_none() + if not product: + raise HTTPException(status_code=400, detail=f"产品不存在: {item_data.product_id}") + else: + if not item_data.product_sku or not item_data.product_name: + raise HTTPException(status_code=400, detail="请提供产品ID,或提供产品SKU与产品名称") + by_sku_result = await db_session.execute( + select(Product).where(Product.sku == item_data.product_sku, Product.is_active == True) + ) + product = by_sku_result.scalar_one_or_none() + if not product: + product = Product( + sku=item_data.product_sku, + name=item_data.product_name, + category=item_data.product_category, + unit=item_data.product_unit or "件", + item_type="finished", + cost_price=0, + sale_price=item_data.unit_price or 0, + min_stock=0, + max_stock=0 + ) + db_session.add(product) + await db_session.flush() + if product.item_type != "finished": + raise HTTPException(status_code=400, detail=f"销售单仅允许成品: {product.name}") + item = SalesOrderItem( + order_id=order.id, + product_id=product.id, + quantity=item_data.quantity, + unit_price=item_data.unit_price, + amount=item_data.quantity * item_data.unit_price, + remark=item_data.remark + ) + db_session.add(item) + total_amount += item.amount + return total_amount + + +class SalesOrderService: + """销售订单业务服务""" + + @staticmethod + async def list_orders( + db_session: AsyncSession, + status: Optional[str], + skip: int, + limit: int, + ) -> PaginatedResponse: + base_query = ( + select(SalesOrder, Customer) + .join(Customer, SalesOrder.customer_id == Customer.id) + .order_by(SalesOrder.created_at.desc()) + ) + + if status: + base_query = base_query.where(SalesOrder.status == status) + + count_query = select(func.count()).select_from(base_query.subquery()) + total = await db_session.scalar(count_query) or 0 + + query = base_query.offset(skip).limit(limit) + result = await db_session.execute(query) + + orders = [] + for order, customer in result.all(): + orders.append(_build_sales_order_response(order, customer.name)) + + return PaginatedResponse(items=orders, total=total, skip=skip, limit=limit) + + @staticmethod + async def create_order( + db_session: AsyncSession, + order_data: SalesOrderCreate, + current_user: User, + ) -> SalesOrderResponse: + now = datetime.now() + order = SalesOrder( + order_no=generate_order_no("SO"), + customer_id=order_data.customer_id, + order_date=now, + delivery_date=order_data.delivery_date, + manufacturing_date=now.date(), + created_at=now, + remark=order_data.remark, + operator_id=current_user.id, + status="manufacturing" + ) + db_session.add(order) + await db_session.flush() + + try: + order.total_amount = await _apply_order_items(db_session, order, order_data) + await _issue_materials_for_order_creation(db_session, order, current_user) + await db_session.commit() + except (HTTPException, Exception): + await db_session.rollback() + raise + await db_session.refresh(order) + + customer = await db_session.execute(select(Customer).where(Customer.id == order.customer_id)) + customer = customer.scalar_one() + + return _build_sales_order_response(order, customer.name) + + @staticmethod + async def get_detail(db_session: AsyncSession, order_id: int) -> SalesOrderDetailResponse: + order, customer = await _get_sales_order_with_customer(db_session, order_id) + return await _build_sales_order_detail_response(db_session, order, customer.name) + + @staticmethod + async def update_order( + db_session: AsyncSession, + order_id: int, + order_data: SalesOrderCreate, + current_user: User, + ) -> SalesOrderResponse: + order, customer = await _get_sales_order_with_customer(db_session, order_id) + if order.status == "paid": + raise HTTPException(status_code=400, detail="已收款的销售订单禁止修改") + try: + await _rollback_issued_materials(db_session, order, current_user) + await db_session.execute(delete(SalesOrderItem).where(SalesOrderItem.order_id == order.id)) + + order.customer_id = order_data.customer_id + order.delivery_date = order_data.delivery_date + order.remark = order_data.remark + order.production_status = "not_started" + order.production_no = None + order.planned_material_cost = 0 + order.actual_material_cost = 0 + order.status = "manufacturing" + + order.total_amount = await _apply_order_items(db_session, order, order_data) + await _issue_materials_for_order_creation(db_session, order, current_user) + await db_session.commit() + except (HTTPException, Exception): + await db_session.rollback() + raise + await db_session.refresh(order) + + customer_result = await db_session.execute(select(Customer).where(Customer.id == order.customer_id)) + updated_customer = customer_result.scalar_one_or_none() + customer_name = updated_customer.name if updated_customer else "未知客户" + return _build_sales_order_response(order, customer_name) + + @staticmethod + async def update_status( + db_session: AsyncSession, + order_id: int, + payload: SalesOrderStatusUpdate, + current_user: User, + ) -> SalesOrderResponse: + if payload.status not in VALID_ORDER_STATUSES: + raise HTTPException(status_code=400, detail="订单状态必须为 manufacturing、delivered、paid") + + order, customer = await _get_sales_order_with_customer(db_session, order_id) + + # 状态转换守卫 + if order.status == "paid": + raise HTTPException(status_code=400, detail="已收款的销售订单禁止修改状态") + if order.status == "cancelled": + raise HTTPException(status_code=400, detail="已作废的销售订单禁止修改状态") + if order.status == "delivered" and payload.status not in ("paid", "cancelled"): + raise HTTPException(status_code=400, detail="已交付的销售订单只能修改为已收款或已作废") + if payload.status == "cancelled" and order.status == "paid": + raise HTTPException(status_code=400, detail="已收款的订单不能作废,请联系管理员") + + # 根据状态更新相应的日期字段 + if payload.status == "delivered" and not order.actual_delivery_date: + order.actual_delivery_date = datetime.now() + elif payload.status == "paid" and not order.actual_payment_date: + order.actual_payment_date = datetime.now() + elif payload.status == "cancelled" and not order.actual_delivery_date: + order.actual_delivery_date = datetime.now() + + order.status = payload.status + await db_session.commit() + await db_session.refresh(order) + return _build_sales_order_response(order, customer.name) + + @staticmethod + async def delete_order(db_session: AsyncSession, order_id: int, current_user: User) -> dict: + order, customer = await _get_sales_order_with_customer(db_session, order_id) + if order.status == "delivered": + raise HTTPException(status_code=400, detail="已交付的销售订单禁止删除") + if order.status == "paid": + raise HTTPException(status_code=400, detail="已收款的销售订单禁止删除,请先作废相关收款单") + if (order.received_amount or 0) > 0: + raise HTTPException(status_code=400, detail="该销售订单已存在收款记录(received_amount>0),禁止删除,请先作废相关收款单") + await _rollback_issued_materials(db_session, order, current_user) + await db_session.delete(order) + await db_session.commit() + return {"message": "销售订单已删除"} + + @staticmethod + async def consume_materials( + db_session: AsyncSession, + order_id: int, + request: MaterialConsumptionRequest, + current_user: User, + ) -> dict: + """记录销售订单的物料消耗(在已发料基础上记录额外消耗,如报废/超耗)""" + order, customer = await _get_sales_order_with_customer(db_session, order_id) + + if order.status in ("delivered", "paid", "cancelled"): + raise HTTPException(status_code=400, detail="当前订单状态不允许记录物料消耗") + if order.production_status == "completed": + raise HTTPException(status_code=400, detail="该销售单已完成生产,不允许记录物料消耗") + + default_warehouse = await _get_default_warehouse(db_session) + + # 计算总物料成本 + total_cost = Decimal("0") + + # 处理每个物料消耗项 + for item in request.items: + # 获取物料信息 + material = await db_session.get(Product, item.material_id) + if not material: + raise HTTPException(status_code=404, detail=f"物料 ID {item.material_id} 不存在") + if material.item_type != "material": + raise HTTPException(status_code=400, detail=f"只能消耗物料类型的产品: {material.name}") + + # 统一转 Decimal,避免 Decimal * float 在 Postgres(Numeric) 上抛 TypeError + consume_qty = Decimal(str(item.quantity)) + unit_cost = Decimal(str(material.cost_price or 0)) + cost = unit_cost * consume_qty + total_cost += cost + + # 更新物料库存(原子操作防并发) + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == material.id) + .where(Inventory.warehouse_id == default_warehouse.id) + .where(Inventory.quantity >= consume_qty) + .values(quantity=Inventory.quantity - consume_qty) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + raise HTTPException(status_code=400, detail=f"物料 {material.name} 库存不足") + after_qty = Decimal(str(after_qty)) + before_qty = after_qty + consume_qty + + # 记录物料消耗 + movement = StockMovement( + product_id=material.id, + warehouse_id=default_warehouse.id, + quantity=consume_qty, + before_quantity=before_qty, + after_quantity=after_qty, + movement_type="consumption", + reference_type="sales_order", + reference_id=order.id, + unit_price=unit_cost, + total_amount=cost, + operator_id=current_user.id, + remark=item.remark + ) + db_session.add(movement) + + # 累加实际物料成本(创建时已记录计划发料成本,此处为额外消耗,不应覆盖) + order.actual_material_cost = Decimal(str(order.actual_material_cost or 0)) + total_cost + + await db_session.commit() + await db_session.refresh(order) + + return { + "message": "物料消耗记录保存成功", + "total_cost": total_cost, + "order": _build_sales_order_response(order, customer.name) + } + + @staticmethod + async def get_production_plan(db_session: AsyncSession, order_id: int) -> SalesOrderProductionPlanResponse: + order, customer = await _get_sales_order_with_customer(db_session, order_id) + production_no = order.production_no or generate_order_no("WO") + plan_items, planned_material_cost = await _build_material_plan(db_session, order) + + return SalesOrderProductionPlanResponse( + sales_order_id=order.id, + order_no=order.order_no, + customer_name=customer.name, + production_no=production_no, + planned_material_cost=planned_material_cost, + items=plan_items, + ) + + @staticmethod + async def issue_materials( + db_session: AsyncSession, + order_id: int, + payload: SalesOrderIssueRequest, + current_user: User, + ) -> SalesOrderIssueResponse: + order, _ = await _get_sales_order_with_customer(db_session, order_id) + if order.status == "delivered": + raise HTTPException(status_code=400, detail="已交付的销售订单禁止领料") + if order.production_status == "completed": + raise HTTPException(status_code=400, detail="该销售单已完成生产") + if order.production_status == "material_issued": + raise HTTPException(status_code=400, detail="该销售单已自动扣减过物料") + + warehouse_result = await db_session.execute( + select(Warehouse).where(Warehouse.id == payload.warehouse_id, Warehouse.is_active == True) + ) + warehouse = warehouse_result.scalar_one_or_none() + if not warehouse: + raise HTTPException(status_code=404, detail="仓库不存在") + + plan_items, planned_material_cost = await _build_material_plan(db_session, order) + if not plan_items: + raise HTTPException(status_code=400, detail="该销售单未配置BOM,无法领料") + + shortage_items = [item for item in plan_items if item.shortage_quantity > 0] + if shortage_items: + shortage_text = ",".join([f"{item.material_name} 缺 {item.shortage_quantity}" for item in shortage_items]) + raise HTTPException(status_code=400, detail=f"物料库存不足:{shortage_text}") + + production_no = payload.production_no or order.production_no or generate_order_no("WO") + + actual_material_cost = Decimal("0") + movement_count = 0 + for item in plan_items: + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == item.material_id) + .where(Inventory.warehouse_id == warehouse.id) + .where(Inventory.quantity >= item.required_quantity) + .values(quantity=Inventory.quantity - item.required_quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + raise HTTPException(status_code=400, detail=f"{item.material_name} 在所选仓库库存不足") + after_qty = int(after_qty) + before_qty = after_qty + int(item.required_quantity) + total_amount = item.required_quantity * item.unit_cost + actual_material_cost += total_amount + + movement = StockMovement( + product_id=item.material_id, + warehouse_id=warehouse.id, + movement_type="issue_to_production", + quantity=item.required_quantity, + before_quantity=before_qty, + after_quantity=after_qty, + reference_type="sales_order", + reference_id=order.id, + reference_no=production_no, + unit_price=item.unit_cost, + total_amount=Decimal(str(total_amount)), + remark=payload.remark or f"销售单{order.order_no}按单生产领料", + operator_id=current_user.id + ) + db_session.add(movement) + movement_count += 1 + + order.production_no = production_no + order.production_status = "material_issued" + order.planned_material_cost = planned_material_cost + order.actual_material_cost = actual_material_cost + if order.status == "draft": + order.status = "manufacturing" + + await db_session.commit() + + cost_deviation = actual_material_cost - planned_material_cost + cost_deviation_rate = (cost_deviation / planned_material_cost) if planned_material_cost > Decimal("1e-9") else Decimal("0") + return SalesOrderIssueResponse( + sales_order_id=order.id, + order_no=order.order_no, + production_no=production_no, + movement_count=movement_count, + planned_material_cost=planned_material_cost, + actual_material_cost=actual_material_cost, + cost_deviation=cost_deviation, + cost_deviation_rate=cost_deviation_rate, + production_status=order.production_status, + ) + + +sales_order_service = SalesOrderService() diff --git a/src/inventory/services/stock_movement_service.py b/src/inventory/services/stock_movement_service.py new file mode 100644 index 0000000..5c2d25a --- /dev/null +++ b/src/inventory/services/stock_movement_service.py @@ -0,0 +1,193 @@ +"""库存变动业务服务层 + +将原 stock_movement_routes 中的业务编排(入库/出库/调整的库存原子更新与流水写入、 +历史查询)下沉到此,路由层只做参数校验与响应组装。 +""" +from typing import Optional + +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, update, func + +from shared.models.database import User, Product, Warehouse, Inventory, StockMovement +from ..schemas import StockMovementCreate, StockMovementResponse, PaginatedResponse +from ..utils import generate_order_no + +INBOUND_TYPES = { + "in", "purchase_in", "return_from_production", "outsource_return", "finish_in" +} +OUTBOUND_TYPES = { + "out", "issue_to_production", "outsource_send", "shipment_out", "scrap_out" +} +ADJUST_TYPES = {"adjust"} +SUPPORTED_MOVEMENT_TYPES = INBOUND_TYPES | OUTBOUND_TYPES | ADJUST_TYPES + + +def _build_movement_response(movement: StockMovement, product: Product) -> StockMovementResponse: + return StockMovementResponse( + id=movement.id, + product_id=product.id, + product_sku=product.sku, + product_name=product.name, + movement_type=movement.movement_type, + quantity=movement.quantity, + before_quantity=movement.before_quantity, + after_quantity=movement.after_quantity, + reference_no=movement.reference_no, + remark=movement.remark, + created_at=movement.created_at + ) + + +class StockMovementService: + """库存变动业务服务""" + + @staticmethod + async def create_movement( + db_session: AsyncSession, + movement_data: StockMovementCreate, + current_user: User, + ) -> StockMovementResponse: + if movement_data.movement_type not in SUPPORTED_MOVEMENT_TYPES: + raise HTTPException(status_code=400, detail="无效的变动类型") + if movement_data.quantity <= 0: + raise HTTPException(status_code=400, detail="数量必须大于0") + + warehouse_result = await db_session.execute( + select(Warehouse) + .where(Warehouse.id == movement_data.warehouse_id) + .where(Warehouse.is_active == True) + ) + warehouse = warehouse_result.scalar_one_or_none() + if not warehouse: + raise HTTPException(status_code=404, detail="仓库不存在") + + product_result = await db_session.execute( + select(Product) + .where(Product.id == movement_data.product_id) + .where(Product.is_active == True) + ) + product = product_result.scalar_one_or_none() + if not product: + sku_candidate = movement_data.product_sku or str(movement_data.product_id) + product_by_sku_result = await db_session.execute( + select(Product) + .where(Product.sku == sku_candidate) + .where(Product.is_active == True) + ) + product = product_by_sku_result.scalar_one_or_none() + if not product: + raise HTTPException(status_code=404, detail="产品不存在,请选择系统中的产品") + if product.item_type != "material": + raise HTTPException(status_code=400, detail="库存仅管理物料,该条目不是物料") + + resolved_product_id = product.id + + if movement_data.movement_type in INBOUND_TYPES: + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == resolved_product_id) + .where(Inventory.warehouse_id == movement_data.warehouse_id) + .values(quantity=Inventory.quantity + movement_data.quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + inventory = Inventory( + product_id=resolved_product_id, + warehouse_id=movement_data.warehouse_id, + quantity=movement_data.quantity, + ) + db_session.add(inventory) + await db_session.flush() + before_qty = 0 + after_qty = movement_data.quantity + else: + after_qty = int(after_qty) + before_qty = after_qty - movement_data.quantity + + elif movement_data.movement_type in OUTBOUND_TYPES: + upd_result = await db_session.execute( + update(Inventory) + .where(Inventory.product_id == resolved_product_id) + .where(Inventory.warehouse_id == movement_data.warehouse_id) + .where(Inventory.quantity >= movement_data.quantity) + .values(quantity=Inventory.quantity - movement_data.quantity) + .returning(Inventory.quantity) + ) + after_qty = upd_result.scalar_one_or_none() + if after_qty is None: + raise HTTPException(status_code=400, detail="库存不足") + after_qty = int(after_qty) + before_qty = after_qty + movement_data.quantity + + else: + result = await db_session.execute( + select(Inventory) + .where(Inventory.product_id == resolved_product_id) + .where(Inventory.warehouse_id == movement_data.warehouse_id) + .with_for_update() + ) + inventory = result.scalar_one_or_none() + if not inventory: + inventory = Inventory( + product_id=resolved_product_id, + warehouse_id=movement_data.warehouse_id, + quantity=0, + ) + db_session.add(inventory) + await db_session.flush() + before_qty = 0 + else: + before_qty = int(inventory.quantity) + inventory.quantity = movement_data.quantity + after_qty = movement_data.quantity + + movement = StockMovement( + product_id=resolved_product_id, + warehouse_id=movement_data.warehouse_id, + movement_type=movement_data.movement_type, + quantity=movement_data.quantity, + before_quantity=before_qty, + after_quantity=after_qty, + reference_no=generate_order_no("SM"), + unit_price=movement_data.unit_price, + total_amount=movement_data.unit_price * movement_data.quantity if movement_data.unit_price else None, + remark=movement_data.remark, + operator_id=current_user.id + ) + db_session.add(movement) + await db_session.commit() + + return _build_movement_response(movement, product) + + @staticmethod + async def list_movements( + db_session: AsyncSession, + product_id: Optional[int], + movement_type: Optional[str], + skip: int, + limit: int, + ) -> PaginatedResponse: + base_query = ( + select(StockMovement, Product) + .join(Product, StockMovement.product_id == Product.id) + .order_by(StockMovement.created_at.desc()) + ) + + if product_id: + base_query = base_query.where(StockMovement.product_id == product_id) + if movement_type: + base_query = base_query.where(StockMovement.movement_type == movement_type) + + count_query = select(func.count()).select_from(base_query.subquery()) + total = await db_session.scalar(count_query) or 0 + + query = base_query.offset(skip).limit(limit) + result = await db_session.execute(query) + + movements = [_build_movement_response(movement, product) for movement, product in result.all()] + return PaginatedResponse(items=movements, total=total, skip=skip, limit=limit) + + +stock_movement_service = StockMovementService() diff --git a/src/moldinsight/core/aluminum_foam_mold.py b/src/moldinsight/core/aluminum_foam_mold.py index 7830f62..63a8595 100644 --- a/src/moldinsight/core/aluminum_foam_mold.py +++ b/src/moldinsight/core/aluminum_foam_mold.py @@ -29,6 +29,7 @@ from shared.models.schemas import create_mold_cavity_data, create_mold_key_info from shared.utils.logger import get_logger from moldinsight.core.base_mold_generator import BaseMoldGenerator from moldinsight.core.side_action_designer import SideActionDesigner +from moldinsight.services.material_service import MaterialService logger = get_logger(__name__) @@ -54,48 +55,6 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator): self.foam_material = foam_material - self.foam_materials = { - "AlSi10Mg": { - "density": 0.45, - "expansion_ratio": 2.5, - "shrinkage_rate": 0.015, - "molding_temp": 380, - "description": "常用铝硅泡沫" - }, - "AlSi12": { - "density": 0.50, - "expansion_ratio": 2.2, - "shrinkage_rate": 0.012, - "molding_temp": 360, - "description": "高强度铝泡沫" - }, - "Pure Al Foam": { - "density": 0.35, - "expansion_ratio": 3.0, - "shrinkage_rate": 0.020, - "molding_temp": 400, - "description": "纯铝泡沫" - }, - "AlSi7Mg": { - "density": 0.40, - "expansion_ratio": 2.8, - "shrinkage_rate": 0.018, - "molding_temp": 390, - "description": "轻质铝镁泡沫" - } - } - - self.plastic_materials = { - "ABS": {"density": 1.05, "shrinkage": 0.005}, - "PP": {"density": 0.90, "shrinkage": 0.016}, - "PC": {"density": 1.20, "shrinkage": 0.005}, - "PE": {"density": 0.95, "shrinkage": 0.025}, - "PS": {"density": 1.05, "shrinkage": 0.004}, - "PA": {"density": 1.14, "shrinkage": 0.015}, - "POM": {"density": 1.42, "shrinkage": 0.020}, - "PMMA": {"density": 1.18, "shrinkage": 0.004} - } - self.parting_line_tolerance = 0.1 self.max_draft_angle = 5.0 self.min_draft_angle = 1.0 @@ -107,21 +66,21 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator): def set_foam_material(self, material: str): """设置铝泡沫材料""" - if material in self.foam_materials: - props = self.foam_materials[material] + if MaterialService.is_foam_material(material): + props = MaterialService.get_material(material) self.foam_material = material self.material_density = props["density"] - self.shrinkage_rate = props["shrinkage_rate"] + self.shrinkage_rate = props["shrinkage"] logger.info(f"铝泡沫材料设置为 {material}, 密度: {props['density']} g/cm³") else: logger.warning(f"未知材料 {material}, 使用当前设置") def set_material(self, material: str): """设置材料(自动识别类型)""" - if material in self.foam_materials: + if MaterialService.is_foam_material(material): self.set_foam_material(material) - elif material in self.plastic_materials: - props = self.plastic_materials[material] + elif MaterialService.has_material(material): + props = MaterialService.get_material(material) self.material_density = props["density"] self.shrinkage_rate = props["shrinkage"] logger.info(f"塑料材料设置为 {material}, 密度: {props['density']} g/cm³") @@ -204,7 +163,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator): parting_geometry = self._extract_parting_surface_geometry(parting_surface) - material_info = self.foam_materials.get(self.foam_material, {}) + material_info = MaterialService.get_material(self.foam_material) detailed_json = { "metadata": { @@ -251,7 +210,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator): def generate_cavity_key_info(self, cavity_data: Dict) -> Dict[str, Any]: """生成模具型腔的关键信息""" analysis = cavity_data["analysis"] - material_info = self.foam_materials.get(self.foam_material, {}) + material_info = MaterialService.get_material(self.foam_material) key_info = { "mold_parameters": { diff --git a/src/moldinsight/core/geometry_analyzer.py b/src/moldinsight/core/geometry_analyzer.py index 482f550..bf18f28 100644 --- a/src/moldinsight/core/geometry_analyzer.py +++ b/src/moldinsight/core/geometry_analyzer.py @@ -9,6 +9,7 @@ from shared.models.schemas import ( create_analysis_result ) from shared.utils.logger import get_logger +from moldinsight.services.material_service import MaterialService logger = get_logger(__name__) @@ -25,12 +26,6 @@ class GeometryAnalyzer: "high_complexity": 50, } - self.product_materials = { - "ABS": {"shrinkage": 0.005, "min_wall": 1.2}, - "PP": {"shrinkage": 0.016, "min_wall": 1.0}, - "PC": {"shrinkage": 0.007, "min_wall": 1.5}, - } - self.mold_materials = { "Aluminum": {"thermal_conductivity": 200, "hardness": "HB80", "cost": "low"}, "P20_Steel": {"thermal_conductivity": 30, "hardness": "HRC30", "cost": "medium"}, @@ -54,7 +49,6 @@ class GeometryAnalyzer: features = self._detect_features(geometry_data, shape) - product_props = self.product_materials.get(product_material, {}) mold_props = self.mold_materials.get(mold_material, {}) recommendations = self._generate_recommendations( @@ -744,8 +738,8 @@ class GeometryAnalyzer: avg_thickness = (volume / surface_area) * 0.6 if avg_thickness is not None: - material_props = self.product_materials.get(material, self.product_materials["ABS"]) - min_wall = material_props["min_wall"] + material_props = MaterialService.get_material(material) + min_wall = material_props.get("min_wall", 1.2) if avg_thickness < min_wall: return create_design_recommendation( diff --git a/src/moldinsight/core/mold_generator.py b/src/moldinsight/core/mold_generator.py index d153413..a884280 100644 --- a/src/moldinsight/core/mold_generator.py +++ b/src/moldinsight/core/mold_generator.py @@ -13,6 +13,7 @@ from shared.models.schemas import create_mold_cavity_data, create_mold_key_info from shared.utils.logger import get_logger from moldinsight.core.base_mold_generator import BaseMoldGenerator from moldinsight.core.side_action_designer import SideActionDesigner +from moldinsight.services.material_service import MaterialService logger = get_logger(__name__) @@ -24,25 +25,14 @@ class MoldCavityGenerator(BaseMoldGenerator): material_density: float = 1.05): super().__init__(shrinkage_rate, draft_angle, material_density) - self.material_densities = { - "ABS": 1.05, - "PP": 0.90, - "PC": 1.20, - "PE": 0.95, - "PS": 1.05, - "PA": 1.14, - "POM": 1.42, - "PMMA": 1.18 - } - self.parting_line_tolerance = 0.1 self.max_draft_angle = 5.0 self.side_action_designer = SideActionDesigner() def set_material(self, material: str): """设置产品材料""" - if material in self.material_densities: - self.material_density = self.material_densities[material] + if MaterialService.has_material(material): + self.material_density = MaterialService.get_material(material)["density"] logger.info(f"材料设置为 {material}, 密度: {self.material_density} g/cm³") else: logger.warning(f"未知材料 {material}, 使用默认密度 {self.material_density} g/cm³") diff --git a/src/moldinsight/services/material_service.py b/src/moldinsight/services/material_service.py index a5c5594..2338707 100644 --- a/src/moldinsight/services/material_service.py +++ b/src/moldinsight/services/material_service.py @@ -1,22 +1,38 @@ # services/material_service.py -"""材料属性管理服务""" +"""材料属性管理服务 - 全局唯一材料属性源 -from typing import Dict, Any, List, Optional +所有材料属性(密度、收缩率、最小壁厚、泡沫特性等)集中在此, +geometry_analyzer / mold_generator / aluminum_foam_mold 等模块一律通过 MaterialService 查询, +禁止再各自维护材料字典,避免数据漂移。 + +历史问题:材料字典曾在 4 处重复定义且冲突(如 PE 收缩率此处 0.020 vs aluminum_foam_mold 0.025; +PC/PA/PMMA 收缩率、POM 密度也冲突;PS 在其他字典有而此处缺失)。现已统一,以本文件为唯一源。 +泡沫材料原用 `shrinkage_rate` 键,现统一为 `shrinkage`(值一致)。 +""" + +from typing import Dict, Any, List MATERIAL_PROPERTIES: Dict[str, Dict[str, Any]] = { - "ABS": {"density": 1.05, "shrinkage": 0.005, "name": "ABS", "is_foam": False}, - "PP": {"density": 0.90, "shrinkage": 0.016, "name": "PP", "is_foam": False}, - "PE": {"density": 0.95, "shrinkage": 0.020, "name": "PE", "is_foam": False}, - "PC": {"density": 1.20, "shrinkage": 0.007, "name": "PC", "is_foam": False}, - "PA": {"density": 1.14, "shrinkage": 0.010, "name": "PA", "is_foam": False}, - "POM": {"density": 1.41, "shrinkage": 0.020, "name": "POM", "is_foam": False}, - "PMMA": {"density": 1.18, "shrinkage": 0.005, "name": "PMMA", "is_foam": False}, - "PBT": {"density": 1.31, "shrinkage": 0.015, "name": "PBT", "is_foam": False}, - "AlSi10Mg": {"density": 0.45, "shrinkage": 0.015, "name": "AlSi10Mg", "is_foam": True}, - "AlSi12": {"density": 0.50, "shrinkage": 0.012, "name": "AlSi12", "is_foam": True}, - "Pure Al Foam": {"density": 0.35, "shrinkage": 0.020, "name": "Pure Al Foam", "is_foam": True}, - "AlSi7Mg": {"density": 0.40, "shrinkage": 0.018, "name": "AlSi7Mg", "is_foam": True}, + # —— 注塑塑料 —— + "ABS": {"name": "ABS", "density": 1.05, "shrinkage": 0.005, "is_foam": False, "min_wall": 1.2}, + "PP": {"name": "PP", "density": 0.90, "shrinkage": 0.016, "is_foam": False, "min_wall": 1.0}, + "PE": {"name": "PE", "density": 0.95, "shrinkage": 0.020, "is_foam": False, "min_wall": 1.0}, + "PC": {"name": "PC", "density": 1.20, "shrinkage": 0.007, "is_foam": False, "min_wall": 1.5}, + "PA": {"name": "PA", "density": 1.14, "shrinkage": 0.010, "is_foam": False, "min_wall": 1.0}, + "POM": {"name": "POM", "density": 1.41, "shrinkage": 0.020, "is_foam": False, "min_wall": 1.0}, + "PMMA": {"name": "PMMA", "density": 1.18, "shrinkage": 0.005, "is_foam": False, "min_wall": 1.5}, + "PBT": {"name": "PBT", "density": 1.31, "shrinkage": 0.015, "is_foam": False, "min_wall": 1.2}, + "PS": {"name": "PS", "density": 1.05, "shrinkage": 0.004, "is_foam": False, "min_wall": 1.0}, + # —— 铝泡沫 —— + "AlSi10Mg": {"name": "AlSi10Mg", "density": 0.45, "shrinkage": 0.015, "is_foam": True, + "expansion_ratio": 2.5, "molding_temp": 380, "description": "常用铝硅泡沫"}, + "AlSi12": {"name": "AlSi12", "density": 0.50, "shrinkage": 0.012, "is_foam": True, + "expansion_ratio": 2.2, "molding_temp": 360, "description": "高强度铝泡沫"}, + "Pure Al Foam": {"name": "Pure Al Foam", "density": 0.35, "shrinkage": 0.020, "is_foam": True, + "expansion_ratio": 3.0, "molding_temp": 400, "description": "纯铝泡沫"}, + "AlSi7Mg": {"name": "AlSi7Mg", "density": 0.40, "shrinkage": 0.018, "is_foam": True, + "expansion_ratio": 2.8, "molding_temp": 390, "description": "轻质铝镁泡沫"}, } # 默认回退材料 @@ -24,13 +40,18 @@ _DEFAULT_MATERIAL = MATERIAL_PROPERTIES["ABS"] class MaterialService: - """材料属性管理服务 — 集中管理材料字典,便于扩展和单测""" + """材料属性管理服务 - 集中管理材料字典,便于扩展和单测""" @staticmethod def get_material(material_name: str) -> Dict[str, Any]: """获取材料属性,不存在则回退到 ABS""" return MATERIAL_PROPERTIES.get(material_name, _DEFAULT_MATERIAL) + @staticmethod + def has_material(material_name: str) -> bool: + """材料是否在字典中(不回退)""" + return material_name in MATERIAL_PROPERTIES + @staticmethod def is_foam_material(material_name: str) -> bool: return MATERIAL_PROPERTIES.get(material_name, _DEFAULT_MATERIAL).get("is_foam", False)