From 7a247bfc1288b6ad88024350d3e12a99b2fe8a75 Mon Sep 17 00:00:00 2001 From: chakcy <947105045@qq.com> Date: Sun, 10 May 2026 12:38:54 +0800 Subject: [PATCH] =?UTF-8?q?modify:=20erp=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.toml | 25 +- src/chongming-web/index.html | 8 +- src/chongming-web/src/App.vue | 171 +- src/chongming-web/src/api/generated/index.ts | 4 +- .../src/api/generated/sdk.gen.ts | 1326 +++++---- .../src/api/generated/types.gen.ts | 2553 ++++++++++++++--- src/chongming-web/src/main.ts | 63 + src/chongming-web/src/router/index.ts | 98 +- src/chongming-web/src/stores/auth.ts | 30 +- src/chongming-web/src/stores/todo.ts | 55 +- src/chongming-web/src/views/LoginView.vue | 547 +++- src/chongming-web/src/views/RegisterView.vue | 383 ++- src/chongming-web/src/views/TodoView.vue | 536 ++-- .../src/views/erp/CodeRuleConfigView.vue | 565 ++++ .../src/views/erp/CustomerView.vue | 155 + .../src/views/erp/DashboardView.vue | 521 ++++ src/chongming-web/src/views/erp/ErpLayout.vue | 595 ++++ .../src/views/erp/InventoryView.vue | 129 + .../src/views/erp/ProductView.vue | 608 ++++ .../src/views/erp/PurchaseInboundView.vue | 527 ++++ .../src/views/erp/PurchaseOrderView.vue | 817 ++++++ .../src/views/erp/SalesOrderView.vue | 166 ++ .../src/views/erp/SalesOutboundView.vue | 165 ++ .../src/views/erp/SupplierView.vue | 157 + .../src/views/erp/WarehouseView.vue | 165 ++ .../src/views/system/PermissionView.vue | 520 ++++ .../src/views/system/RoleView.vue | 652 +++++ .../src/views/system/UserView.vue | 678 +++++ src/chongming-web/vite.config.ts | 10 + src/chongming/app/api/__init__.py | 15 +- src/chongming/app/api/deps.py | 13 +- src/chongming/app/api/routers/erp/__init__.py | 31 + .../app/api/routers/erp/customer_router.py | 70 + .../app/api/routers/erp/inventory_router.py | 108 + .../app/api/routers/erp/product_router.py | 70 + .../routers/erp/purchase_inbound_router.py | 71 + .../app/api/routers/erp/purchase_router.py | 79 + .../api/routers/erp/sales_outbound_router.py | 74 + .../app/api/routers/erp/sales_router.py | 78 + .../app/api/routers/erp/supplier_router.py | 70 + .../app/api/routers/erp/warehouse_router.py | 70 + .../app/api/routers/system/__init__.py | 17 + .../{auth.py => system/auth_router.py} | 174 +- .../api/routers/system/code_rule_router.py | 136 + .../permission_router.py} | 64 +- .../{role.py => system/role_router.py} | 41 +- .../{upload.py => system/upload_router.py} | 7 +- src/chongming/app/api/routers/todo.py | 126 - .../app/constant/permission/__init__.py | 50 +- .../app/constant/permission/erp/__init__.py | 66 + .../app/constant/permission/erp/customer.py | 13 + .../app/constant/permission/erp/inventory.py | 11 + .../app/constant/permission/erp/product.py | 13 + .../app/constant/permission/erp/purchase.py | 15 + .../permission/erp/purchase_inbound.py | 15 + .../app/constant/permission/erp/sales.py | 15 + .../constant/permission/erp/sales_outbound.py | 15 + .../app/constant/permission/erp/supplier.py | 13 + .../app/constant/permission/erp/warehouse.py | 13 + .../constant/permission/system/__init__.py | 99 + .../permission/{ => system}/permission_mgt.py | 0 .../permission/{ => system}/personnel.py | 0 .../constant/permission/{ => system}/role.py | 0 .../permission/{ => system}/system_config.py | 0 src/chongming/app/constant/permission/todo.py | 14 - src/chongming/app/core/order_no_generator.py | 210 ++ src/chongming/app/model/__init__.py | 69 +- src/chongming/app/model/erp/__init__.py | 37 + src/chongming/app/model/erp/code_rule.py | 64 + src/chongming/app/model/erp/customer.py | 66 + src/chongming/app/model/erp/inventory.py | 87 + .../app/model/erp/order_no_sequence.py | 32 + src/chongming/app/model/erp/product.py | 89 + src/chongming/app/model/erp/purchase.py | 89 + .../app/model/erp/purchase_inbound.py | 85 + src/chongming/app/model/erp/sales.py | 89 + src/chongming/app/model/erp/sales_outbound.py | 85 + src/chongming/app/model/erp/supplier.py | 66 + src/chongming/app/model/erp/warehouse.py | 58 + src/chongming/app/model/system/__init__.py | 0 .../app/model/{ => system}/http_error.py | 0 .../app/model/{ => system}/permission.py | 0 .../model/{ => system}/relationship_table.py | 0 src/chongming/app/model/{ => system}/role.py | 0 src/chongming/app/model/{ => system}/user.py | 44 + src/chongming/app/model/todo.py | 75 - src/chongming/app/service/erp/__init__.py | 21 + .../app/service/erp/code_rule_service.py | 113 + .../app/service/erp/customer_service.py | 64 + .../app/service/erp/inventory_service.py | 135 + .../app/service/erp/product_service.py | 65 + .../service/erp/purchase_inbound_service.py | 192 ++ .../app/service/erp/purchase_service.py | 165 ++ .../app/service/erp/sales_outbound_service.py | 201 ++ .../app/service/erp/sales_service.py | 163 ++ .../app/service/erp/supplier_service.py | 64 + .../app/service/erp/warehouse_service.py | 62 + src/chongming/app/service/system/__init__.py | 0 .../app/service/{ => system}/auth.py | 4 +- .../app/service/{ => system}/permission.py | 62 +- .../app/service/{ => system}/role.py | 4 +- .../app/service/{ => system}/uploads.py | 3 +- src/chongming/app/service/system/user.py | 176 ++ src/chongming/app/service/todo.py | 121 - src/chongming/app/service/user.py | 68 - src/chongming/app/task/dev_init_db.py | 76 +- src/chongming/scripts/build.py | 147 +- src/chongming/scripts/code_content.py | 1 + src/plugins/jwt/jwt_cache.py | 39 +- uv.lock | 416 +-- 110 files changed, 15536 insertions(+), 1966 deletions(-) create mode 100644 src/chongming-web/src/views/erp/CodeRuleConfigView.vue create mode 100644 src/chongming-web/src/views/erp/CustomerView.vue create mode 100644 src/chongming-web/src/views/erp/DashboardView.vue create mode 100644 src/chongming-web/src/views/erp/ErpLayout.vue create mode 100644 src/chongming-web/src/views/erp/InventoryView.vue create mode 100644 src/chongming-web/src/views/erp/ProductView.vue create mode 100644 src/chongming-web/src/views/erp/PurchaseInboundView.vue create mode 100644 src/chongming-web/src/views/erp/PurchaseOrderView.vue create mode 100644 src/chongming-web/src/views/erp/SalesOrderView.vue create mode 100644 src/chongming-web/src/views/erp/SalesOutboundView.vue create mode 100644 src/chongming-web/src/views/erp/SupplierView.vue create mode 100644 src/chongming-web/src/views/erp/WarehouseView.vue create mode 100644 src/chongming-web/src/views/system/PermissionView.vue create mode 100644 src/chongming-web/src/views/system/RoleView.vue create mode 100644 src/chongming-web/src/views/system/UserView.vue create mode 100644 src/chongming/app/api/routers/erp/__init__.py create mode 100644 src/chongming/app/api/routers/erp/customer_router.py create mode 100644 src/chongming/app/api/routers/erp/inventory_router.py create mode 100644 src/chongming/app/api/routers/erp/product_router.py create mode 100644 src/chongming/app/api/routers/erp/purchase_inbound_router.py create mode 100644 src/chongming/app/api/routers/erp/purchase_router.py create mode 100644 src/chongming/app/api/routers/erp/sales_outbound_router.py create mode 100644 src/chongming/app/api/routers/erp/sales_router.py create mode 100644 src/chongming/app/api/routers/erp/supplier_router.py create mode 100644 src/chongming/app/api/routers/erp/warehouse_router.py create mode 100644 src/chongming/app/api/routers/system/__init__.py rename src/chongming/app/api/routers/{auth.py => system/auth_router.py} (62%) create mode 100644 src/chongming/app/api/routers/system/code_rule_router.py rename src/chongming/app/api/routers/{permission.py => system/permission_router.py} (85%) rename src/chongming/app/api/routers/{role.py => system/role_router.py} (89%) rename src/chongming/app/api/routers/{upload.py => system/upload_router.py} (94%) delete mode 100644 src/chongming/app/api/routers/todo.py create mode 100644 src/chongming/app/constant/permission/erp/__init__.py create mode 100644 src/chongming/app/constant/permission/erp/customer.py create mode 100644 src/chongming/app/constant/permission/erp/inventory.py create mode 100644 src/chongming/app/constant/permission/erp/product.py create mode 100644 src/chongming/app/constant/permission/erp/purchase.py create mode 100644 src/chongming/app/constant/permission/erp/purchase_inbound.py create mode 100644 src/chongming/app/constant/permission/erp/sales.py create mode 100644 src/chongming/app/constant/permission/erp/sales_outbound.py create mode 100644 src/chongming/app/constant/permission/erp/supplier.py create mode 100644 src/chongming/app/constant/permission/erp/warehouse.py create mode 100644 src/chongming/app/constant/permission/system/__init__.py rename src/chongming/app/constant/permission/{ => system}/permission_mgt.py (100%) rename src/chongming/app/constant/permission/{ => system}/personnel.py (100%) rename src/chongming/app/constant/permission/{ => system}/role.py (100%) rename src/chongming/app/constant/permission/{ => system}/system_config.py (100%) delete mode 100644 src/chongming/app/constant/permission/todo.py create mode 100644 src/chongming/app/core/order_no_generator.py create mode 100644 src/chongming/app/model/erp/__init__.py create mode 100644 src/chongming/app/model/erp/code_rule.py create mode 100644 src/chongming/app/model/erp/customer.py create mode 100644 src/chongming/app/model/erp/inventory.py create mode 100644 src/chongming/app/model/erp/order_no_sequence.py create mode 100644 src/chongming/app/model/erp/product.py create mode 100644 src/chongming/app/model/erp/purchase.py create mode 100644 src/chongming/app/model/erp/purchase_inbound.py create mode 100644 src/chongming/app/model/erp/sales.py create mode 100644 src/chongming/app/model/erp/sales_outbound.py create mode 100644 src/chongming/app/model/erp/supplier.py create mode 100644 src/chongming/app/model/erp/warehouse.py create mode 100644 src/chongming/app/model/system/__init__.py rename src/chongming/app/model/{ => system}/http_error.py (100%) rename src/chongming/app/model/{ => system}/permission.py (100%) rename src/chongming/app/model/{ => system}/relationship_table.py (100%) rename src/chongming/app/model/{ => system}/role.py (100%) rename src/chongming/app/model/{ => system}/user.py (74%) delete mode 100644 src/chongming/app/model/todo.py create mode 100644 src/chongming/app/service/erp/__init__.py create mode 100644 src/chongming/app/service/erp/code_rule_service.py create mode 100644 src/chongming/app/service/erp/customer_service.py create mode 100644 src/chongming/app/service/erp/inventory_service.py create mode 100644 src/chongming/app/service/erp/product_service.py create mode 100644 src/chongming/app/service/erp/purchase_inbound_service.py create mode 100644 src/chongming/app/service/erp/purchase_service.py create mode 100644 src/chongming/app/service/erp/sales_outbound_service.py create mode 100644 src/chongming/app/service/erp/sales_service.py create mode 100644 src/chongming/app/service/erp/supplier_service.py create mode 100644 src/chongming/app/service/erp/warehouse_service.py create mode 100644 src/chongming/app/service/system/__init__.py rename src/chongming/app/service/{ => system}/auth.py (99%) rename src/chongming/app/service/{ => system}/permission.py (88%) rename src/chongming/app/service/{ => system}/role.py (98%) rename src/chongming/app/service/{ => system}/uploads.py (98%) create mode 100644 src/chongming/app/service/system/user.py delete mode 100644 src/chongming/app/service/todo.py delete mode 100644 src/chongming/app/service/user.py diff --git a/config.toml b/config.toml index f618b4a..013eea5 100644 --- a/config.toml +++ b/config.toml @@ -27,22 +27,41 @@ pool_timeout = 30 job_store_path = "scheduler_jobs.db" [cache] -cache_store_path = "./" +cache_store_path = "./cache_data" [security] -access_token_expire_minutes = 30 +access_token_expire_minutes = 1440 algorithm = "HS256" max_sessions_per_user = 5 refresh_token_expire_days = 7 secret_key = "your-secret-key-change-in-production" +# ============================================ +# 订单号自动生成规则配置 +# 每种订单类型可以独立配置生成规则 +# 格式: {prefix}{date}{seq} 例如 PO202405080001 +# prefix: 订单前缀 +# date_format: 日期格式(Python strftime 格式) +# seq_width: 序号位数(自动补零) +# separator: 各部分之间的分隔符(可选) +# ============================================ +[order_no] +# 采购订单: PO202405080001 +purchase_order = { prefix = "PO", date_format = "%Y%m%d", seq_width = 4, separator = "" } +# 销售订单: SO202405080001 +sales_order = { prefix = "SO", date_format = "%Y%m%d", seq_width = 4, separator = "" } +# 采购入库单: PI202405080001 +purchase_inbound = { prefix = "PI", date_format = "%Y%m%d", seq_width = 4, separator = "" } +# 销售出库单: DO202405080001 +sales_outbound = { prefix = "DO", date_format = "%Y%m%d", seq_width = 4, separator = "" } + [development] debug = true env = "development" [development.server] access_log = false -host = "127.0.0.1" +host = "0.0.0.0" port = 8000 reload = true diff --git a/src/chongming-web/index.html b/src/chongming-web/index.html index 9e5fc8f..e40720c 100644 --- a/src/chongming-web/index.html +++ b/src/chongming-web/index.html @@ -1,10 +1,14 @@ - + - Vite App + 崇明·ERP - 进销存管理系统 + + + +
diff --git a/src/chongming-web/src/App.vue b/src/chongming-web/src/App.vue index 5e07a41..564713c 100644 --- a/src/chongming-web/src/App.vue +++ b/src/chongming-web/src/App.vue @@ -4,13 +4,176 @@ import { RouterView } from 'vue-router' \ No newline at end of file + +html, body, #app { + height: 100%; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', + Arial, sans-serif; + font-size: 14px; + color: #303133; + background: #f5f7fa; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* ========== 全局过渡动画 ========== */ +.page-fade-enter-active, +.page-fade-leave-active { + transition: opacity 0.25s ease, transform 0.25s ease; +} +.page-fade-enter-from { + opacity: 0; + transform: translateY(12px); +} +.page-fade-leave-to { + opacity: 0; + transform: translateY(-8px); +} + +/* ========== 自定义滚动条 ========== */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: #c0c4cc; + border-radius: 3px; + transition: background 0.2s; +} +::-webkit-scrollbar-thumb:hover { + background: #909399; +} + +/* ========== Element Plus 全局覆盖 ========== */ +.el-card { + border-radius: 12px !important; + border: 1px solid #ebeef5 !important; + transition: box-shadow 0.3s ease; +} +.el-card:hover { + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04); +} + +.el-dialog { + border-radius: 16px !important; +} +.el-dialog__header { + padding: 24px 28px 0 !important; +} +.el-dialog__body { + padding: 20px 28px !important; +} +.el-dialog__footer { + padding: 0 28px 24px !important; +} + +.el-button { + border-radius: 8px !important; + transition: all 0.25s ease !important; +} +.el-button--primary { + --el-button-bg-color: #6366f1; + --el-button-border-color: #6366f1; + --el-button-hover-bg-color: #4f46e5; + --el-button-hover-border-color: #4f46e5; + --el-button-active-bg-color: #4338ca; + --el-button-active-border-color: #4338ca; +} + +.el-table { + border-radius: 8px; + overflow: hidden; +} +.el-table th.el-table__cell { + background-color: #f8f9fc !important; + color: #606266; + font-weight: 600; + font-size: 13px; +} +.el-table--striped .el-table__body tr.el-table__row--striped td { + background-color: #fafbfd; +} + +.el-tag { + border-radius: 6px !important; + font-weight: 500; +} + +.el-pagination { + --el-pagination-button-bg-color: transparent; +} + +.el-input__wrapper, +.el-input-number__wrapper { + border-radius: 8px !important; + box-shadow: 0 0 0 1px #dcdfe6 inset !important; + transition: box-shadow 0.2s ease !important; +} +.el-input__wrapper:hover, +.el-input-number__wrapper:hover { + box-shadow: 0 0 0 1px #c0c4cc inset !important; +} +.el-input__wrapper.is-focus, +.el-input-number__wrapper.is-focus { + box-shadow: 0 0 0 1px #6366f1 inset !important; +} + +.el-switch.is-checked .el-switch__core { + border-color: #6366f1 !important; + background-color: #6366f1 !important; +} + +.el-empty { + padding: 40px 0; +} + +/* ========== 工具类 ========== */ +.text-gradient { + background: linear-gradient(135deg, #6366f1, #8b5cf6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.card-hover { + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; +} +.card-hover:hover { + transform: translateY(-4px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.08) !important; +} + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.glass-effect { + background: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.8); +} + diff --git a/src/chongming-web/src/api/generated/index.ts b/src/chongming-web/src/api/generated/index.ts index 13ce1c8..d6fed03 100644 --- a/src/chongming-web/src/api/generated/index.ts +++ b/src/chongming-web/src/api/generated/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { assignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPost, assignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPost, assignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPost, assignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPost, createPermissionApiV1PermissionsPost, createRoleApiV1RolesPost, createTodoApiV1TodosPost, deletePermissionApiV1PermissionsPermissionIdDelete, deleteRoleApiV1RolesRoleIdDelete, deleteTodoApiV1TodosTodoIdDelete, getCurrentUserInfoApiV1AuthMeGet, getPermissionApiV1PermissionsPermissionIdGet, getRoleApiV1RolesRoleIdGet, getRolePermissionsApiV1RolesRoleIdPermissionsGet, getSessionsApiV1AuthSessionsGet, listPermissionsApiV1PermissionsGet, listRolesApiV1RolesGet, loginApiV1AuthLoginPost, logoutAllApiV1AuthLogoutAllPost, logoutApiV1AuthLogoutPost, type Options, readTodoApiV1TodosTodoIdGet, readTodosApiV1TodosGet, refreshTokenApiV1AuthRefreshPost, registerApiV1AuthRegisterPost, rootGet, unbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPost, unbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPost, unbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPost, unbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPost, updatePermissionApiV1PermissionsPermissionIdPut, updateRoleApiV1RolesRoleIdPut, updateTodoApiV1TodosTodoIdPatch, uploadImageApiV1UploadImagePost } from './sdk.gen'; -export type { AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostData, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostError, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostData, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostError, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostResponses, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostData, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostError, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostError, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostResponses, BodyUploadImageApiV1UploadImagePost, ClientOptions, CreatePermissionApiV1PermissionsPostData, CreatePermissionApiV1PermissionsPostError, CreatePermissionApiV1PermissionsPostErrors, CreatePermissionApiV1PermissionsPostResponse, CreatePermissionApiV1PermissionsPostResponses, CreateRoleApiV1RolesPostData, CreateRoleApiV1RolesPostError, CreateRoleApiV1RolesPostErrors, CreateRoleApiV1RolesPostResponse, CreateRoleApiV1RolesPostResponses, CreateTodoApiV1TodosPostData, CreateTodoApiV1TodosPostError, CreateTodoApiV1TodosPostErrors, CreateTodoApiV1TodosPostResponse, CreateTodoApiV1TodosPostResponses, DeletePermissionApiV1PermissionsPermissionIdDeleteData, DeletePermissionApiV1PermissionsPermissionIdDeleteError, DeletePermissionApiV1PermissionsPermissionIdDeleteErrors, DeletePermissionApiV1PermissionsPermissionIdDeleteResponses, DeleteRoleApiV1RolesRoleIdDeleteData, DeleteRoleApiV1RolesRoleIdDeleteError, DeleteRoleApiV1RolesRoleIdDeleteErrors, DeleteRoleApiV1RolesRoleIdDeleteResponses, DeleteTodoApiV1TodosTodoIdDeleteData, DeleteTodoApiV1TodosTodoIdDeleteError, DeleteTodoApiV1TodosTodoIdDeleteErrors, DeleteTodoApiV1TodosTodoIdDeleteResponses, Detail, GetCurrentUserInfoApiV1AuthMeGetData, GetCurrentUserInfoApiV1AuthMeGetError, GetCurrentUserInfoApiV1AuthMeGetErrors, GetCurrentUserInfoApiV1AuthMeGetResponse, GetCurrentUserInfoApiV1AuthMeGetResponses, GetPermissionApiV1PermissionsPermissionIdGetData, GetPermissionApiV1PermissionsPermissionIdGetError, GetPermissionApiV1PermissionsPermissionIdGetErrors, GetPermissionApiV1PermissionsPermissionIdGetResponse, GetPermissionApiV1PermissionsPermissionIdGetResponses, GetRoleApiV1RolesRoleIdGetData, GetRoleApiV1RolesRoleIdGetError, GetRoleApiV1RolesRoleIdGetErrors, GetRoleApiV1RolesRoleIdGetResponse, GetRoleApiV1RolesRoleIdGetResponses, GetRolePermissionsApiV1RolesRoleIdPermissionsGetData, GetRolePermissionsApiV1RolesRoleIdPermissionsGetError, GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors, GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponse, GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses, GetSessionsApiV1AuthSessionsGetData, GetSessionsApiV1AuthSessionsGetResponses, HttpError, HttpValidationError, ListPermissionsApiV1PermissionsGetData, ListPermissionsApiV1PermissionsGetError, ListPermissionsApiV1PermissionsGetErrors, ListPermissionsApiV1PermissionsGetResponse, ListPermissionsApiV1PermissionsGetResponses, ListRolesApiV1RolesGetData, ListRolesApiV1RolesGetError, ListRolesApiV1RolesGetErrors, ListRolesApiV1RolesGetResponse, ListRolesApiV1RolesGetResponses, LoginApiV1AuthLoginPostData, LoginApiV1AuthLoginPostError, LoginApiV1AuthLoginPostErrors, LoginApiV1AuthLoginPostResponse, LoginApiV1AuthLoginPostResponses, LogoutAllApiV1AuthLogoutAllPostData, LogoutAllApiV1AuthLogoutAllPostResponse, LogoutAllApiV1AuthLogoutAllPostResponses, LogoutApiV1AuthLogoutPostData, LogoutApiV1AuthLogoutPostResponse, LogoutApiV1AuthLogoutPostResponses, Permission, PermissionCreate, PermissionUpdate, ReadTodoApiV1TodosTodoIdGetData, ReadTodoApiV1TodosTodoIdGetError, ReadTodoApiV1TodosTodoIdGetErrors, ReadTodoApiV1TodosTodoIdGetResponse, ReadTodoApiV1TodosTodoIdGetResponses, ReadTodosApiV1TodosGetData, ReadTodosApiV1TodosGetError, ReadTodosApiV1TodosGetErrors, ReadTodosApiV1TodosGetResponse, ReadTodosApiV1TodosGetResponses, RefreshTokenApiV1AuthRefreshPostData, RefreshTokenApiV1AuthRefreshPostError, RefreshTokenApiV1AuthRefreshPostErrors, RefreshTokenApiV1AuthRefreshPostResponse, RefreshTokenApiV1AuthRefreshPostResponses, RefreshTokenResponse, RegisterApiV1AuthRegisterPostData, RegisterApiV1AuthRegisterPostError, RegisterApiV1AuthRegisterPostErrors, RegisterApiV1AuthRegisterPostResponse, RegisterApiV1AuthRegisterPostResponses, Role, RoleUpdate, RootGetData, RootGetResponses, TodoCreate, TodoRead, TodoUpdate, TokenResponse, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostData, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostError, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostData, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostError, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostResponses, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostData, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostError, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostError, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostResponses, UpdatePermissionApiV1PermissionsPermissionIdPutData, UpdatePermissionApiV1PermissionsPermissionIdPutError, UpdatePermissionApiV1PermissionsPermissionIdPutErrors, UpdatePermissionApiV1PermissionsPermissionIdPutResponse, UpdatePermissionApiV1PermissionsPermissionIdPutResponses, UpdateRoleApiV1RolesRoleIdPutData, UpdateRoleApiV1RolesRoleIdPutError, UpdateRoleApiV1RolesRoleIdPutErrors, UpdateRoleApiV1RolesRoleIdPutResponse, UpdateRoleApiV1RolesRoleIdPutResponses, UpdateTodoApiV1TodosTodoIdPatchData, UpdateTodoApiV1TodosTodoIdPatchError, UpdateTodoApiV1TodosTodoIdPatchErrors, UpdateTodoApiV1TodosTodoIdPatchResponse, UpdateTodoApiV1TodosTodoIdPatchResponses, UploadImageApiV1UploadImagePostData, UploadImageApiV1UploadImagePostError, UploadImageApiV1UploadImagePostErrors, UploadImageApiV1UploadImagePostResponse, UploadImageApiV1UploadImagePostResponses, UploadResponse, UserCreate, UserLogin, Userlogout, UserRead, ValidationError } from './types.gen'; +export { adjustInventoryApiV1ErpInventoryAdjustPost, approvePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePost, approveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePost, assignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPost, assignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPost, assignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPost, createCustomerApiV1ErpCustomersPost, createPermissionApiV1SystemPermissionPost, createProductApiV1ErpProductsPost, createPurchaseInboundApiV1ErpPurchaseInboundsPost, createPurchaseOrderApiV1ErpPurchaseOrdersPost, createRoleApiV1SystemRolePost, createSalesOrderApiV1ErpSalesOrdersPost, createSalesOutboundApiV1ErpSalesOutboundsPost, createSupplierApiV1ErpSuppliersPost, createWarehouseApiV1ErpWarehousesPost, deleteCustomerApiV1ErpCustomersCustomerIdDelete, deletePermissionApiV1SystemPermissionPermissionIdDelete, deleteProductApiV1ErpProductsProductIdDelete, deletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDelete, deletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDelete, deleteRoleApiV1SystemRoleRoleIdDelete, deleteSalesOrderApiV1ErpSalesOrdersOrderIdDelete, deleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDelete, deleteSupplierApiV1ErpSuppliersSupplierIdDelete, deleteWarehouseApiV1ErpWarehousesWarehouseIdDelete, getCurrentUserInfoApiV1SystemAuthMeGet, getPermissionApiV1SystemPermissionPermissionIdGet, getRoleApiV1SystemRoleRoleIdGet, getRolePermissionsApiV1SystemRoleRoleIdPermissionsGet, getSessionsApiV1SystemAuthSessionsGet, listPermissionsApiV1SystemPermissionGet, listRolesApiV1SystemRoleGet, loginApiV1SystemAuthLoginPost, logoutAllApiV1SystemAuthLogoutAllPost, logoutApiV1SystemAuthLogoutPost, type Options, readCustomerApiV1ErpCustomersCustomerIdGet, readCustomersApiV1ErpCustomersGet, readInventoryApiV1ErpInventoryGet, readInventoryByProductApiV1ErpInventoryProductProductIdGet, readMovementsApiV1ErpInventoryMovementsGet, readProductApiV1ErpProductsProductIdGet, readProductsApiV1ErpProductsGet, readPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGet, readPurchaseInboundsApiV1ErpPurchaseInboundsGet, readPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGet, readPurchaseOrdersApiV1ErpPurchaseOrdersGet, readSalesOrderApiV1ErpSalesOrdersOrderIdGet, readSalesOrdersApiV1ErpSalesOrdersGet, readSalesOutboundApiV1ErpSalesOutboundsOutboundIdGet, readSalesOutboundsApiV1ErpSalesOutboundsGet, readSupplierApiV1ErpSuppliersSupplierIdGet, readSuppliersApiV1ErpSuppliersGet, readWarehouseApiV1ErpWarehousesWarehouseIdGet, readWarehousesApiV1ErpWarehousesGet, refreshTokenApiV1SystemAuthRefreshPost, registerApiV1SystemAuthRegisterPost, rootGet, unbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPost, unbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPost, unbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPost, updateCustomerApiV1ErpCustomersCustomerIdPatch, updatePermissionApiV1SystemPermissionPermissionIdPut, updateProductApiV1ErpProductsProductIdPatch, updatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatch, updateRoleApiV1SystemRoleRoleIdPut, updateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatch, updateSupplierApiV1ErpSuppliersSupplierIdPatch, updateWarehouseApiV1ErpWarehousesWarehouseIdPatch, uploadImageApiV1SystemUploadImagePost } from './sdk.gen'; +export type { AdjustInventoryApiV1ErpInventoryAdjustPostData, AdjustInventoryApiV1ErpInventoryAdjustPostError, AdjustInventoryApiV1ErpInventoryAdjustPostErrors, AdjustInventoryApiV1ErpInventoryAdjustPostResponses, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostData, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostError, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostErrors, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostResponses, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostData, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostError, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostErrors, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostResponses, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostData, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostError, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostErrors, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostData, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostError, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostErrors, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostData, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostError, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostErrors, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostResponses, BodyUploadImageApiV1SystemUploadImagePost, ClientOptions, CreateCustomerApiV1ErpCustomersPostData, CreateCustomerApiV1ErpCustomersPostError, CreateCustomerApiV1ErpCustomersPostErrors, CreateCustomerApiV1ErpCustomersPostResponse, CreateCustomerApiV1ErpCustomersPostResponses, CreatePermissionApiV1SystemPermissionPostData, CreatePermissionApiV1SystemPermissionPostError, CreatePermissionApiV1SystemPermissionPostErrors, CreatePermissionApiV1SystemPermissionPostResponse, CreatePermissionApiV1SystemPermissionPostResponses, CreateProductApiV1ErpProductsPostData, CreateProductApiV1ErpProductsPostError, CreateProductApiV1ErpProductsPostErrors, CreateProductApiV1ErpProductsPostResponse, CreateProductApiV1ErpProductsPostResponses, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostData, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostError, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostErrors, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostResponses, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostData, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostError, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostErrors, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostResponses, CreateRoleApiV1SystemRolePostData, CreateRoleApiV1SystemRolePostError, CreateRoleApiV1SystemRolePostErrors, CreateRoleApiV1SystemRolePostResponse, CreateRoleApiV1SystemRolePostResponses, CreateSalesOrderApiV1ErpSalesOrdersPostData, CreateSalesOrderApiV1ErpSalesOrdersPostError, CreateSalesOrderApiV1ErpSalesOrdersPostErrors, CreateSalesOrderApiV1ErpSalesOrdersPostResponses, CreateSalesOutboundApiV1ErpSalesOutboundsPostData, CreateSalesOutboundApiV1ErpSalesOutboundsPostError, CreateSalesOutboundApiV1ErpSalesOutboundsPostErrors, CreateSalesOutboundApiV1ErpSalesOutboundsPostResponses, CreateSupplierApiV1ErpSuppliersPostData, CreateSupplierApiV1ErpSuppliersPostError, CreateSupplierApiV1ErpSuppliersPostErrors, CreateSupplierApiV1ErpSuppliersPostResponse, CreateSupplierApiV1ErpSuppliersPostResponses, CreateWarehouseApiV1ErpWarehousesPostData, CreateWarehouseApiV1ErpWarehousesPostError, CreateWarehouseApiV1ErpWarehousesPostErrors, CreateWarehouseApiV1ErpWarehousesPostResponse, CreateWarehouseApiV1ErpWarehousesPostResponses, CustomerCreate, CustomerRead, CustomerUpdate, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteData, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteError, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteErrors, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteResponses, DeletePermissionApiV1SystemPermissionPermissionIdDeleteData, DeletePermissionApiV1SystemPermissionPermissionIdDeleteError, DeletePermissionApiV1SystemPermissionPermissionIdDeleteErrors, DeletePermissionApiV1SystemPermissionPermissionIdDeleteResponses, DeleteProductApiV1ErpProductsProductIdDeleteData, DeleteProductApiV1ErpProductsProductIdDeleteError, DeleteProductApiV1ErpProductsProductIdDeleteErrors, DeleteProductApiV1ErpProductsProductIdDeleteResponses, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteData, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteError, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteErrors, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteResponses, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteData, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteError, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteErrors, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteResponses, DeleteRoleApiV1SystemRoleRoleIdDeleteData, DeleteRoleApiV1SystemRoleRoleIdDeleteError, DeleteRoleApiV1SystemRoleRoleIdDeleteErrors, DeleteRoleApiV1SystemRoleRoleIdDeleteResponses, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteData, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteError, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteErrors, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteResponses, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteData, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteError, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteErrors, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteResponses, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteData, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteError, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteErrors, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteResponses, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteData, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteError, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteErrors, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteResponses, Detail, GetCurrentUserInfoApiV1SystemAuthMeGetData, GetCurrentUserInfoApiV1SystemAuthMeGetError, GetCurrentUserInfoApiV1SystemAuthMeGetErrors, GetCurrentUserInfoApiV1SystemAuthMeGetResponse, GetCurrentUserInfoApiV1SystemAuthMeGetResponses, GetPermissionApiV1SystemPermissionPermissionIdGetData, GetPermissionApiV1SystemPermissionPermissionIdGetError, GetPermissionApiV1SystemPermissionPermissionIdGetErrors, GetPermissionApiV1SystemPermissionPermissionIdGetResponse, GetPermissionApiV1SystemPermissionPermissionIdGetResponses, GetRoleApiV1SystemRoleRoleIdGetData, GetRoleApiV1SystemRoleRoleIdGetError, GetRoleApiV1SystemRoleRoleIdGetErrors, GetRoleApiV1SystemRoleRoleIdGetResponse, GetRoleApiV1SystemRoleRoleIdGetResponses, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetData, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetError, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetErrors, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponse, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponses, GetSessionsApiV1SystemAuthSessionsGetData, GetSessionsApiV1SystemAuthSessionsGetResponses, HttpError, HttpValidationError, InventoryAdjustRequest, InventoryMovementRead, InventoryRead, ListPermissionsApiV1SystemPermissionGetData, ListPermissionsApiV1SystemPermissionGetError, ListPermissionsApiV1SystemPermissionGetErrors, ListPermissionsApiV1SystemPermissionGetResponse, ListPermissionsApiV1SystemPermissionGetResponses, ListRolesApiV1SystemRoleGetData, ListRolesApiV1SystemRoleGetError, ListRolesApiV1SystemRoleGetErrors, ListRolesApiV1SystemRoleGetResponse, ListRolesApiV1SystemRoleGetResponses, LoginApiV1SystemAuthLoginPostData, LoginApiV1SystemAuthLoginPostError, LoginApiV1SystemAuthLoginPostErrors, LoginApiV1SystemAuthLoginPostResponse, LoginApiV1SystemAuthLoginPostResponses, LogoutAllApiV1SystemAuthLogoutAllPostData, LogoutAllApiV1SystemAuthLogoutAllPostResponse, LogoutAllApiV1SystemAuthLogoutAllPostResponses, LogoutApiV1SystemAuthLogoutPostData, LogoutApiV1SystemAuthLogoutPostResponse, LogoutApiV1SystemAuthLogoutPostResponses, Permission, PermissionCreate, PermissionUpdate, ProductCreate, ProductRead, ProductUpdate, PurchaseInboundCreate, PurchaseInboundItemCreate, PurchaseOrderCreate, PurchaseOrderItemCreate, PurchaseOrderUpdate, ReadCustomerApiV1ErpCustomersCustomerIdGetData, ReadCustomerApiV1ErpCustomersCustomerIdGetError, ReadCustomerApiV1ErpCustomersCustomerIdGetErrors, ReadCustomerApiV1ErpCustomersCustomerIdGetResponse, ReadCustomerApiV1ErpCustomersCustomerIdGetResponses, ReadCustomersApiV1ErpCustomersGetData, ReadCustomersApiV1ErpCustomersGetError, ReadCustomersApiV1ErpCustomersGetErrors, ReadCustomersApiV1ErpCustomersGetResponse, ReadCustomersApiV1ErpCustomersGetResponses, ReadInventoryApiV1ErpInventoryGetData, ReadInventoryApiV1ErpInventoryGetError, ReadInventoryApiV1ErpInventoryGetErrors, ReadInventoryApiV1ErpInventoryGetResponse, ReadInventoryApiV1ErpInventoryGetResponses, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetData, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetError, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetErrors, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetResponses, ReadMovementsApiV1ErpInventoryMovementsGetData, ReadMovementsApiV1ErpInventoryMovementsGetError, ReadMovementsApiV1ErpInventoryMovementsGetErrors, ReadMovementsApiV1ErpInventoryMovementsGetResponse, ReadMovementsApiV1ErpInventoryMovementsGetResponses, ReadProductApiV1ErpProductsProductIdGetData, ReadProductApiV1ErpProductsProductIdGetError, ReadProductApiV1ErpProductsProductIdGetErrors, ReadProductApiV1ErpProductsProductIdGetResponse, ReadProductApiV1ErpProductsProductIdGetResponses, ReadProductsApiV1ErpProductsGetData, ReadProductsApiV1ErpProductsGetError, ReadProductsApiV1ErpProductsGetErrors, ReadProductsApiV1ErpProductsGetResponse, ReadProductsApiV1ErpProductsGetResponses, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetData, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetError, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetErrors, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetResponses, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetData, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetError, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetErrors, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetResponses, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetData, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetError, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetErrors, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetResponses, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetData, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetError, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetErrors, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetResponses, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetData, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetError, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetErrors, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetResponses, ReadSalesOrdersApiV1ErpSalesOrdersGetData, ReadSalesOrdersApiV1ErpSalesOrdersGetError, ReadSalesOrdersApiV1ErpSalesOrdersGetErrors, ReadSalesOrdersApiV1ErpSalesOrdersGetResponses, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetData, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetError, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetErrors, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetResponses, ReadSalesOutboundsApiV1ErpSalesOutboundsGetData, ReadSalesOutboundsApiV1ErpSalesOutboundsGetError, ReadSalesOutboundsApiV1ErpSalesOutboundsGetErrors, ReadSalesOutboundsApiV1ErpSalesOutboundsGetResponses, ReadSupplierApiV1ErpSuppliersSupplierIdGetData, ReadSupplierApiV1ErpSuppliersSupplierIdGetError, ReadSupplierApiV1ErpSuppliersSupplierIdGetErrors, ReadSupplierApiV1ErpSuppliersSupplierIdGetResponse, ReadSupplierApiV1ErpSuppliersSupplierIdGetResponses, ReadSuppliersApiV1ErpSuppliersGetData, ReadSuppliersApiV1ErpSuppliersGetError, ReadSuppliersApiV1ErpSuppliersGetErrors, ReadSuppliersApiV1ErpSuppliersGetResponse, ReadSuppliersApiV1ErpSuppliersGetResponses, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetData, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetError, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetErrors, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponse, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponses, ReadWarehousesApiV1ErpWarehousesGetData, ReadWarehousesApiV1ErpWarehousesGetError, ReadWarehousesApiV1ErpWarehousesGetErrors, ReadWarehousesApiV1ErpWarehousesGetResponse, ReadWarehousesApiV1ErpWarehousesGetResponses, RefreshTokenApiV1SystemAuthRefreshPostData, RefreshTokenApiV1SystemAuthRefreshPostError, RefreshTokenApiV1SystemAuthRefreshPostErrors, RefreshTokenApiV1SystemAuthRefreshPostResponse, RefreshTokenApiV1SystemAuthRefreshPostResponses, RefreshTokenResponse, RegisterApiV1SystemAuthRegisterPostData, RegisterApiV1SystemAuthRegisterPostError, RegisterApiV1SystemAuthRegisterPostErrors, RegisterApiV1SystemAuthRegisterPostResponse, RegisterApiV1SystemAuthRegisterPostResponses, Role, RoleUpdate, RootGetData, RootGetResponses, SalesOrderCreate, SalesOrderItemCreate, SalesOrderUpdate, SalesOutboundCreate, SalesOutboundItemCreate, SupplierCreate, SupplierRead, SupplierUpdate, TokenResponse, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostData, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostError, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostErrors, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostData, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostError, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostErrors, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostData, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostError, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostErrors, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostResponses, UpdateCustomerApiV1ErpCustomersCustomerIdPatchData, UpdateCustomerApiV1ErpCustomersCustomerIdPatchError, UpdateCustomerApiV1ErpCustomersCustomerIdPatchErrors, UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponse, UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponses, UpdatePermissionApiV1SystemPermissionPermissionIdPutData, UpdatePermissionApiV1SystemPermissionPermissionIdPutError, UpdatePermissionApiV1SystemPermissionPermissionIdPutErrors, UpdatePermissionApiV1SystemPermissionPermissionIdPutResponse, UpdatePermissionApiV1SystemPermissionPermissionIdPutResponses, UpdateProductApiV1ErpProductsProductIdPatchData, UpdateProductApiV1ErpProductsProductIdPatchError, UpdateProductApiV1ErpProductsProductIdPatchErrors, UpdateProductApiV1ErpProductsProductIdPatchResponse, UpdateProductApiV1ErpProductsProductIdPatchResponses, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchData, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchError, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchErrors, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchResponses, UpdateRoleApiV1SystemRoleRoleIdPutData, UpdateRoleApiV1SystemRoleRoleIdPutError, UpdateRoleApiV1SystemRoleRoleIdPutErrors, UpdateRoleApiV1SystemRoleRoleIdPutResponse, UpdateRoleApiV1SystemRoleRoleIdPutResponses, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchData, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchError, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchErrors, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchResponses, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchData, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchError, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchErrors, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponse, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponses, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchData, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchError, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchErrors, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponse, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponses, UploadImageApiV1SystemUploadImagePostData, UploadImageApiV1SystemUploadImagePostError, UploadImageApiV1SystemUploadImagePostErrors, UploadImageApiV1SystemUploadImagePostResponse, UploadImageApiV1SystemUploadImagePostResponses, UploadResponse, UserCreate, UserLogin, Userlogout, UserRead, ValidationError, WarehouseCreate, WarehouseRead, WarehouseUpdate } from './types.gen'; diff --git a/src/chongming-web/src/api/generated/sdk.gen.ts b/src/chongming-web/src/api/generated/sdk.gen.ts index 4f54ea1..da4441c 100644 --- a/src/chongming-web/src/api/generated/sdk.gen.ts +++ b/src/chongming-web/src/api/generated/sdk.gen.ts @@ -2,7 +2,7 @@ import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client'; import { client } from './client.gen'; -import type { AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostData, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostData, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostResponses, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostData, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostResponses, CreatePermissionApiV1PermissionsPostData, CreatePermissionApiV1PermissionsPostErrors, CreatePermissionApiV1PermissionsPostResponses, CreateRoleApiV1RolesPostData, CreateRoleApiV1RolesPostErrors, CreateRoleApiV1RolesPostResponses, CreateTodoApiV1TodosPostData, CreateTodoApiV1TodosPostErrors, CreateTodoApiV1TodosPostResponses, DeletePermissionApiV1PermissionsPermissionIdDeleteData, DeletePermissionApiV1PermissionsPermissionIdDeleteErrors, DeletePermissionApiV1PermissionsPermissionIdDeleteResponses, DeleteRoleApiV1RolesRoleIdDeleteData, DeleteRoleApiV1RolesRoleIdDeleteErrors, DeleteRoleApiV1RolesRoleIdDeleteResponses, DeleteTodoApiV1TodosTodoIdDeleteData, DeleteTodoApiV1TodosTodoIdDeleteErrors, DeleteTodoApiV1TodosTodoIdDeleteResponses, GetCurrentUserInfoApiV1AuthMeGetData, GetCurrentUserInfoApiV1AuthMeGetErrors, GetCurrentUserInfoApiV1AuthMeGetResponses, GetPermissionApiV1PermissionsPermissionIdGetData, GetPermissionApiV1PermissionsPermissionIdGetErrors, GetPermissionApiV1PermissionsPermissionIdGetResponses, GetRoleApiV1RolesRoleIdGetData, GetRoleApiV1RolesRoleIdGetErrors, GetRoleApiV1RolesRoleIdGetResponses, GetRolePermissionsApiV1RolesRoleIdPermissionsGetData, GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors, GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses, GetSessionsApiV1AuthSessionsGetData, GetSessionsApiV1AuthSessionsGetResponses, ListPermissionsApiV1PermissionsGetData, ListPermissionsApiV1PermissionsGetErrors, ListPermissionsApiV1PermissionsGetResponses, ListRolesApiV1RolesGetData, ListRolesApiV1RolesGetErrors, ListRolesApiV1RolesGetResponses, LoginApiV1AuthLoginPostData, LoginApiV1AuthLoginPostErrors, LoginApiV1AuthLoginPostResponses, LogoutAllApiV1AuthLogoutAllPostData, LogoutAllApiV1AuthLogoutAllPostResponses, LogoutApiV1AuthLogoutPostData, LogoutApiV1AuthLogoutPostResponses, ReadTodoApiV1TodosTodoIdGetData, ReadTodoApiV1TodosTodoIdGetErrors, ReadTodoApiV1TodosTodoIdGetResponses, ReadTodosApiV1TodosGetData, ReadTodosApiV1TodosGetErrors, ReadTodosApiV1TodosGetResponses, RefreshTokenApiV1AuthRefreshPostData, RefreshTokenApiV1AuthRefreshPostErrors, RefreshTokenApiV1AuthRefreshPostResponses, RegisterApiV1AuthRegisterPostData, RegisterApiV1AuthRegisterPostErrors, RegisterApiV1AuthRegisterPostResponses, RootGetData, RootGetResponses, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostData, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostData, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostResponses, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostData, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostResponses, UpdatePermissionApiV1PermissionsPermissionIdPutData, UpdatePermissionApiV1PermissionsPermissionIdPutErrors, UpdatePermissionApiV1PermissionsPermissionIdPutResponses, UpdateRoleApiV1RolesRoleIdPutData, UpdateRoleApiV1RolesRoleIdPutErrors, UpdateRoleApiV1RolesRoleIdPutResponses, UpdateTodoApiV1TodosTodoIdPatchData, UpdateTodoApiV1TodosTodoIdPatchErrors, UpdateTodoApiV1TodosTodoIdPatchResponses, UploadImageApiV1UploadImagePostData, UploadImageApiV1UploadImagePostErrors, UploadImageApiV1UploadImagePostResponses } from './types.gen'; +import type { AdjustInventoryApiV1ErpInventoryAdjustPostData, AdjustInventoryApiV1ErpInventoryAdjustPostErrors, AdjustInventoryApiV1ErpInventoryAdjustPostResponses, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostData, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostErrors, ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostResponses, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostData, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostErrors, ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostResponses, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostData, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostErrors, AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostData, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostErrors, AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostData, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostErrors, AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostResponses, CreateCustomerApiV1ErpCustomersPostData, CreateCustomerApiV1ErpCustomersPostErrors, CreateCustomerApiV1ErpCustomersPostResponses, CreatePermissionApiV1SystemPermissionPostData, CreatePermissionApiV1SystemPermissionPostErrors, CreatePermissionApiV1SystemPermissionPostResponses, CreateProductApiV1ErpProductsPostData, CreateProductApiV1ErpProductsPostErrors, CreateProductApiV1ErpProductsPostResponses, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostData, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostErrors, CreatePurchaseInboundApiV1ErpPurchaseInboundsPostResponses, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostData, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostErrors, CreatePurchaseOrderApiV1ErpPurchaseOrdersPostResponses, CreateRoleApiV1SystemRolePostData, CreateRoleApiV1SystemRolePostErrors, CreateRoleApiV1SystemRolePostResponses, CreateSalesOrderApiV1ErpSalesOrdersPostData, CreateSalesOrderApiV1ErpSalesOrdersPostErrors, CreateSalesOrderApiV1ErpSalesOrdersPostResponses, CreateSalesOutboundApiV1ErpSalesOutboundsPostData, CreateSalesOutboundApiV1ErpSalesOutboundsPostErrors, CreateSalesOutboundApiV1ErpSalesOutboundsPostResponses, CreateSupplierApiV1ErpSuppliersPostData, CreateSupplierApiV1ErpSuppliersPostErrors, CreateSupplierApiV1ErpSuppliersPostResponses, CreateWarehouseApiV1ErpWarehousesPostData, CreateWarehouseApiV1ErpWarehousesPostErrors, CreateWarehouseApiV1ErpWarehousesPostResponses, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteData, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteErrors, DeleteCustomerApiV1ErpCustomersCustomerIdDeleteResponses, DeletePermissionApiV1SystemPermissionPermissionIdDeleteData, DeletePermissionApiV1SystemPermissionPermissionIdDeleteErrors, DeletePermissionApiV1SystemPermissionPermissionIdDeleteResponses, DeleteProductApiV1ErpProductsProductIdDeleteData, DeleteProductApiV1ErpProductsProductIdDeleteErrors, DeleteProductApiV1ErpProductsProductIdDeleteResponses, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteData, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteErrors, DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteResponses, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteData, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteErrors, DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteResponses, DeleteRoleApiV1SystemRoleRoleIdDeleteData, DeleteRoleApiV1SystemRoleRoleIdDeleteErrors, DeleteRoleApiV1SystemRoleRoleIdDeleteResponses, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteData, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteErrors, DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteResponses, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteData, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteErrors, DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteResponses, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteData, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteErrors, DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteResponses, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteData, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteErrors, DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteResponses, GetCurrentUserInfoApiV1SystemAuthMeGetData, GetCurrentUserInfoApiV1SystemAuthMeGetErrors, GetCurrentUserInfoApiV1SystemAuthMeGetResponses, GetPermissionApiV1SystemPermissionPermissionIdGetData, GetPermissionApiV1SystemPermissionPermissionIdGetErrors, GetPermissionApiV1SystemPermissionPermissionIdGetResponses, GetRoleApiV1SystemRoleRoleIdGetData, GetRoleApiV1SystemRoleRoleIdGetErrors, GetRoleApiV1SystemRoleRoleIdGetResponses, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetData, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetErrors, GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponses, GetSessionsApiV1SystemAuthSessionsGetData, GetSessionsApiV1SystemAuthSessionsGetResponses, ListPermissionsApiV1SystemPermissionGetData, ListPermissionsApiV1SystemPermissionGetErrors, ListPermissionsApiV1SystemPermissionGetResponses, ListRolesApiV1SystemRoleGetData, ListRolesApiV1SystemRoleGetErrors, ListRolesApiV1SystemRoleGetResponses, LoginApiV1SystemAuthLoginPostData, LoginApiV1SystemAuthLoginPostErrors, LoginApiV1SystemAuthLoginPostResponses, LogoutAllApiV1SystemAuthLogoutAllPostData, LogoutAllApiV1SystemAuthLogoutAllPostResponses, LogoutApiV1SystemAuthLogoutPostData, LogoutApiV1SystemAuthLogoutPostResponses, ReadCustomerApiV1ErpCustomersCustomerIdGetData, ReadCustomerApiV1ErpCustomersCustomerIdGetErrors, ReadCustomerApiV1ErpCustomersCustomerIdGetResponses, ReadCustomersApiV1ErpCustomersGetData, ReadCustomersApiV1ErpCustomersGetErrors, ReadCustomersApiV1ErpCustomersGetResponses, ReadInventoryApiV1ErpInventoryGetData, ReadInventoryApiV1ErpInventoryGetErrors, ReadInventoryApiV1ErpInventoryGetResponses, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetData, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetErrors, ReadInventoryByProductApiV1ErpInventoryProductProductIdGetResponses, ReadMovementsApiV1ErpInventoryMovementsGetData, ReadMovementsApiV1ErpInventoryMovementsGetErrors, ReadMovementsApiV1ErpInventoryMovementsGetResponses, ReadProductApiV1ErpProductsProductIdGetData, ReadProductApiV1ErpProductsProductIdGetErrors, ReadProductApiV1ErpProductsProductIdGetResponses, ReadProductsApiV1ErpProductsGetData, ReadProductsApiV1ErpProductsGetErrors, ReadProductsApiV1ErpProductsGetResponses, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetData, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetErrors, ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetResponses, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetData, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetErrors, ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetResponses, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetData, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetErrors, ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetResponses, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetData, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetErrors, ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetResponses, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetData, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetErrors, ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetResponses, ReadSalesOrdersApiV1ErpSalesOrdersGetData, ReadSalesOrdersApiV1ErpSalesOrdersGetErrors, ReadSalesOrdersApiV1ErpSalesOrdersGetResponses, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetData, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetErrors, ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetResponses, ReadSalesOutboundsApiV1ErpSalesOutboundsGetData, ReadSalesOutboundsApiV1ErpSalesOutboundsGetErrors, ReadSalesOutboundsApiV1ErpSalesOutboundsGetResponses, ReadSupplierApiV1ErpSuppliersSupplierIdGetData, ReadSupplierApiV1ErpSuppliersSupplierIdGetErrors, ReadSupplierApiV1ErpSuppliersSupplierIdGetResponses, ReadSuppliersApiV1ErpSuppliersGetData, ReadSuppliersApiV1ErpSuppliersGetErrors, ReadSuppliersApiV1ErpSuppliersGetResponses, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetData, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetErrors, ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponses, ReadWarehousesApiV1ErpWarehousesGetData, ReadWarehousesApiV1ErpWarehousesGetErrors, ReadWarehousesApiV1ErpWarehousesGetResponses, RefreshTokenApiV1SystemAuthRefreshPostData, RefreshTokenApiV1SystemAuthRefreshPostErrors, RefreshTokenApiV1SystemAuthRefreshPostResponses, RegisterApiV1SystemAuthRegisterPostData, RegisterApiV1SystemAuthRegisterPostErrors, RegisterApiV1SystemAuthRegisterPostResponses, RootGetData, RootGetResponses, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostData, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostErrors, UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostData, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostErrors, UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostData, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostErrors, UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostResponses, UpdateCustomerApiV1ErpCustomersCustomerIdPatchData, UpdateCustomerApiV1ErpCustomersCustomerIdPatchErrors, UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponses, UpdatePermissionApiV1SystemPermissionPermissionIdPutData, UpdatePermissionApiV1SystemPermissionPermissionIdPutErrors, UpdatePermissionApiV1SystemPermissionPermissionIdPutResponses, UpdateProductApiV1ErpProductsProductIdPatchData, UpdateProductApiV1ErpProductsProductIdPatchErrors, UpdateProductApiV1ErpProductsProductIdPatchResponses, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchData, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchErrors, UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchResponses, UpdateRoleApiV1SystemRoleRoleIdPutData, UpdateRoleApiV1SystemRoleRoleIdPutErrors, UpdateRoleApiV1SystemRoleRoleIdPutResponses, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchData, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchErrors, UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchResponses, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchData, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchErrors, UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponses, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchData, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchErrors, UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponses, UploadImageApiV1SystemUploadImagePostData, UploadImageApiV1SystemUploadImagePostErrors, UploadImageApiV1SystemUploadImagePostResponses } from './types.gen'; export type Options = Options2 & { /** @@ -18,112 +18,6 @@ export type Options; }; -/** - * Read Todos - * - * 获取待办事项列表 - * - * Args: - * offset: 分页偏移量,默认为0,用于指定从第几条记录开始查询 - * limit: 每页返回的最大记录数,默认为100,用于限制返回结果的数量 - * session: 数据库会话对象,用于执行数据库操作 - * - * Returns: - * List[TodoRead]: 待办事项列表,包含指定范围内的所有待办事项 - */ -export const readTodosApiV1TodosGet = (options?: Options) => (options?.client ?? client).get({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/todos/', - ...options -}); - -/** - * Create Todo - * - * 创建新的待办事项 - * - * Args: - * todo: 待办事项创建请求体,包含待办事项的详细信息 - * session: 数据库会话对象,用于执行数据库操作 - * - * Returns: - * TodoRead: 创建成功的待办事项对象,包含完整的待办事项信息 - */ -export const createTodoApiV1TodosPost = (options: Options) => (options.client ?? client).post({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/todos/', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } -}); - -/** - * Delete Todo - * - * 删除待办事项 - * - * Args: - * todo_id: 待办事项的唯一标识符 - * session: 数据库会话对象,用于执行数据库操作 - * - * Returns: - * dict: 包含操作结果的字典,{"ok": True} 表示删除成功 - * - * Raises: - * HTTPException: 当待办事项不存在时抛出404错误 - */ -export const deleteTodoApiV1TodosTodoIdDelete = (options: Options) => (options.client ?? client).delete({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/todos/{todo_id}', - ...options -}); - -/** - * Read Todo - * - * 根据ID获取单个待办事项 - * - * Args: - * todo_id: 待办事项的唯一标识符 - * session: 数据库会话对象,用于执行数据库操作 - * - * Returns: - * TodoRead: 查询到的待办事项对象 - * - * Raises: - * HTTPException: 当待办事项不存在时抛出404错误 - */ -export const readTodoApiV1TodosTodoIdGet = (options: Options) => (options.client ?? client).get({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/todos/{todo_id}', - ...options -}); - -/** - * Update Todo - * - * 更新待办事项信息 - * - * Args: - * todo_id: 待办事项的唯一标识符 - * todo_update: 待办事项更新请求体,包含需要更新的字段信息 - * session: 数据库会话对象,用于执行数据库操作 - * - * Returns: - * TodoRead: 更新后的待办事项对象,包含最新的待办事项信息 - */ -export const updateTodoApiV1TodosTodoIdPatch = (options: Options) => (options.client ?? client).patch({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/todos/{todo_id}', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } -}); - /** * Register * @@ -141,8 +35,8 @@ export const updateTodoApiV1TodosTodoIdPatch = (options: Options) => (options.client ?? client).post({ - url: '/api/v1/auth/register', +export const registerApiV1SystemAuthRegisterPost = (options: Options) => (options.client ?? client).post({ + url: '/api/v1/system/auth/register', ...options, headers: { 'Content-Type': 'application/json', @@ -171,8 +65,8 @@ export const registerApiV1AuthRegisterPost = (options: Options) => (options.client ?? client).post({ - url: '/api/v1/auth/login', +export const loginApiV1SystemAuthLoginPost = (options: Options) => (options.client ?? client).post({ + url: '/api/v1/system/auth/login', ...options, headers: { 'Content-Type': 'application/json', @@ -198,9 +92,9 @@ export const loginApiV1AuthLoginPost = (op * Raises: * HTTPException: 当刷新令牌无效或已过期时抛出401错误 */ -export const refreshTokenApiV1AuthRefreshPost = (options?: Options) => (options?.client ?? client).post({ +export const refreshTokenApiV1SystemAuthRefreshPost = (options?: Options) => (options?.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/auth/refresh', + url: '/api/v1/system/auth/refresh', ...options }); @@ -217,9 +111,9 @@ export const refreshTokenApiV1AuthRefreshPost = (options?: Options) => (options?.client ?? client).post({ +export const logoutApiV1SystemAuthLogoutPost = (options?: Options) => (options?.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/auth/logout', + url: '/api/v1/system/auth/logout', ...options }); @@ -236,9 +130,9 @@ export const logoutApiV1AuthLogoutPost = ( * Returns: * Userlogout: 登出成功响应,包含成功消息和状态码200 */ -export const logoutAllApiV1AuthLogoutAllPost = (options?: Options) => (options?.client ?? client).post({ +export const logoutAllApiV1SystemAuthLogoutAllPost = (options?: Options) => (options?.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/auth/logout-all', + url: '/api/v1/system/auth/logout-all', ...options }); @@ -255,9 +149,9 @@ export const logoutAllApiV1AuthLogoutAllPost = (options?: Options) => (options?.client ?? client).get({ +export const getSessionsApiV1SystemAuthSessionsGet = (options?: Options) => (options?.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/auth/sessions', + url: '/api/v1/system/auth/sessions', ...options }); @@ -278,9 +172,431 @@ export const getSessionsApiV1AuthSessionsGet = (options?: Options) => (options?.client ?? client).get({ +export const getCurrentUserInfoApiV1SystemAuthMeGet = (options?: Options) => (options?.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/auth/me', + url: '/api/v1/system/auth/me', + ...options +}); + +/** + * 获取权限列表 + * + * 获取权限列表 + * + * 返回系统中的所有权限项,支持分页和按资源名称筛选。 + * + * Args: + * skip: 跳过的记录数,用于分页 + * limit: 返回的最大记录数,用于分页 + * resource: 资源名称筛选条件,可选 + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * list[Permission]: 权限对象列表 + */ +export const listPermissionsApiV1SystemPermissionGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission', + ...options +}); + +/** + * 创建权限 + * + * 创建权限 + * + * 创建新的权限项,权限名称格式应为"资源:操作",如"user:create"。 + * + * Args: + * permission_data: 权限创建数据,包含名称、资源、操作和描述等信息 + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Permission: 创建成功的权限对象 + * + * Raises: + * HTTPException: 当权限名称已存在时抛出400错误 + */ +export const createPermissionApiV1SystemPermissionPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * 删除权限 + * + * 删除权限 + * + * 删除指定权限。如果权限已被分配给角色,则无法删除。 + * + * Args: + * permission_id: 权限ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 删除成功响应消息 + * + * Raises: + * HTTPException: 当权限被角色使用时抛出400错误,权限不存在时抛出404错误 + */ +export const deletePermissionApiV1SystemPermissionPermissionIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}', + ...options +}); + +/** + * 获取权限详情 + * + * 获取权限详情 + * + * 根据权限ID获取指定权限的详细信息。 + * + * Args: + * permission_id: 权限ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Permission: 权限对象 + * + * Raises: + * HTTPException: 当权限不存在时抛出404错误 + */ +export const getPermissionApiV1SystemPermissionPermissionIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}', + ...options +}); + +/** + * 更新权限 + * + * 更新权限信息 + * + * 更新指定权限的名称、描述、资源或操作等信息。 + * + * Args: + * permission_id: 权限ID + * permission_update: 权限更新数据,所有字段可选 + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Permission: 更新后的权限对象 + * + * Raises: + * HTTPException: 当权限不存在时抛出404错误 + */ +export const updatePermissionApiV1SystemPermissionPermissionIdPut = (options: Options) => (options.client ?? client).put({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * 将权限分配给角色 + * + * 将权限分配给角色 + * + * 将指定权限分配给指定角色。 + * + * Args: + * permission_id: 权限ID + * role_id: 角色ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 分配成功响应消息 + * + * Raises: + * HTTPException: 当权限已分配给角色或角色/权限不存在时抛出错误 + */ +export const assignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}/assign-to-role/{role_id}', + ...options +}); + +/** + * 从角色解除权限绑定 + * + * 从角色解除权限绑定 + * + * 将指定权限从指定角色中移除。 + * + * Args: + * permission_id: 权限ID + * role_id: 角色ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 解绑成功响应消息 + * + * Raises: + * HTTPException: 当角色不存在时抛出404错误 + */ +export const unbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}/unbind-from-role/{role_id}', + ...options +}); + +/** + * 将权限分配给用户 + * + * 将权限分配给用户 + * + * 将指定权限分配给指定用户。如果用户没有专属角色,则会自动创建一个专属角色。 + * + * Args: + * permission_id: 权限ID + * user_id: 用户UUID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 分配成功响应消息 + * + * Raises: + * HTTPException: 当用户或权限不存在时抛出404错误 + */ +export const assignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}/assign-to-user/{user_id}', + ...options +}); + +/** + * 从用户解除权限绑定 + * + * 从用户解除权限绑定 + * + * 将指定权限从指定用户中移除。 + * + * Args: + * permission_id: 权限ID + * user_id: 用户UUID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 解绑成功响应消息 + * + * Raises: + * HTTPException: 当用户或角色不存在时抛出404错误 + */ +export const unbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/permission/{permission_id}/unbind-from-user/{user_id}', + ...options +}); + +/** + * 获取角色列表 + * + * 获取角色列表 + * + * 返回系统中的所有角色,支持分页。 + * + * Args: + * skip: 跳过的记录数,用于分页 + * limit: 返回的最大记录数,用于分页 + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * list[Role]: 角色对象列表 + */ +export const listRolesApiV1SystemRoleGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role', + ...options +}); + +/** + * 创建角色 + * + * 创建角色 + * + * 创建新的角色,如 admin、manager、operator 等。 + * + * Args: + * name: 角色名称 + * description: 角色描述,可选 + * is_only_user: 是否仅用于单个用户,默认为False + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Role: 创建成功的角色对象 + * + * Raises: + * HTTPException: 当角色名称已存在时抛出400错误 + */ +export const createRoleApiV1SystemRolePost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role', + ...options +}); + +/** + * 删除角色 + * + * 删除角色 + * + * 删除指定角色。如果角色已被分配给用户,则无法删除。 + * + * Args: + * role_id: 角色ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 删除成功响应消息 + * + * Raises: + * HTTPException: 当角色被用户使用时抛出400错误,角色不存在时抛出404错误 + */ +export const deleteRoleApiV1SystemRoleRoleIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}', + ...options +}); + +/** + * 获取角色详情 + * + * 获取角色详情 + * + * 根据角色ID获取指定角色的详细信息。 + * + * Args: + * role_id: 角色ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Role: 角色对象 + * + * Raises: + * HTTPException: 当角色不存在时抛出404错误 + */ +export const getRoleApiV1SystemRoleRoleIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}', + ...options +}); + +/** + * 更新角色 + * + * 更新角色信息 + * + * 更新指定角色的名称或描述等信息。 + * + * Args: + * role_id: 角色ID + * role_update: 角色更新数据,所有字段可选 + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * Role: 更新后的角色对象 + * + * Raises: + * HTTPException: 当角色不存在时抛出404错误 + */ +export const updateRoleApiV1SystemRoleRoleIdPut = (options: Options) => (options.client ?? client).put({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * 将角色分配给用户 + * + * 将角色分配给用户 + * + * 将指定角色分配给指定用户。 + * + * Args: + * role_id: 角色ID + * user_id: 用户UUID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 分配成功响应消息 + * + * Raises: + * HTTPException: 当角色或用户不存在时抛出404错误 + */ +export const assignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}/assign-to-user/{user_id}', + ...options +}); + +/** + * 从用户解除角色绑定 + * + * 从用户解除角色绑定 + * + * 将指定角色从指定用户中移除。 + * + * Args: + * role_id: 角色ID + * user_id: 用户UUID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * dict: 解绑成功响应消息 + * + * Raises: + * HTTPException: 当用户不存在时抛出404错误 + */ +export const unbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}/unbind-from-user/{user_id}', + ...options +}); + +/** + * 获取角色的权限列表 + * + * 获取指定角色的所有权限 + * + * Args: + * role_id: 角色ID + * session: 数据库会话对象 + * current_user: 当前登录用户 + * + * Returns: + * list: 权限对象列表 + */ +export const getRolePermissionsApiV1SystemRoleRoleIdPermissionsGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/system/role/{role_id}/permissions', ...options }); @@ -311,10 +627,10 @@ export const getCurrentUserInfoApiV1AuthMeGet = (options: Options) => (options.client ?? client).post({ +export const uploadImageApiV1SystemUploadImagePost = (options: Options) => (options.client ?? client).post({ ...formDataBodySerializer, security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/upload/image', + url: '/api/v1/system/upload/image', ...options, headers: { 'Content-Type': null, @@ -323,49 +639,20 @@ export const uploadImageApiV1UploadImagePost = (options?: Options) => (options?.client ?? client).get({ +export const readProductsApiV1ErpProductsGet = (options?: Options) => (options?.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions', + url: '/api/v1/erp/products/', ...options }); /** - * 创建权限 - * - * 创建权限 - * - * 创建新的权限项,权限名称格式应为"资源:操作",如"user:create"。 - * - * Args: - * permission_data: 权限创建数据,包含名称、资源、操作和描述等信息 - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Permission: 创建成功的权限对象 - * - * Raises: - * HTTPException: 当权限名称已存在时抛出400错误 + * Create Product */ -export const createPermissionApiV1PermissionsPost = (options: Options) => (options.client ?? client).post({ +export const createProductApiV1ErpProductsPost = (options: Options) => (options.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions', + url: '/api/v1/erp/products/', ...options, headers: { 'Content-Type': 'application/json', @@ -374,75 +661,29 @@ export const createPermissionApiV1PermissionsPost = (options: Options) => (options.client ?? client).delete({ +export const deleteProductApiV1ErpProductsProductIdDelete = (options: Options) => (options.client ?? client).delete({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}', + url: '/api/v1/erp/products/{product_id}', ...options }); /** - * 获取权限详情 - * - * 获取权限详情 - * - * 根据权限ID获取指定权限的详细信息。 - * - * Args: - * permission_id: 权限ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Permission: 权限对象 - * - * Raises: - * HTTPException: 当权限不存在时抛出404错误 + * Read Product */ -export const getPermissionApiV1PermissionsPermissionIdGet = (options: Options) => (options.client ?? client).get({ +export const readProductApiV1ErpProductsProductIdGet = (options: Options) => (options.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}', + url: '/api/v1/erp/products/{product_id}', ...options }); /** - * 更新权限 - * - * 更新权限信息 - * - * 更新指定权限的名称、描述、资源或操作等信息。 - * - * Args: - * permission_id: 权限ID - * permission_update: 权限更新数据,所有字段可选 - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Permission: 更新后的权限对象 - * - * Raises: - * HTTPException: 当权限不存在时抛出404错误 + * Update Product */ -export const updatePermissionApiV1PermissionsPermissionIdPut = (options: Options) => (options.client ?? client).put({ +export const updateProductApiV1ErpProductsProductIdPatch = (options: Options) => (options.client ?? client).patch({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}', + url: '/api/v1/erp/products/{product_id}', ...options, headers: { 'Content-Type': 'application/json', @@ -451,223 +692,20 @@ export const updatePermissionApiV1PermissionsPermissionIdPut = (options: Options) => (options.client ?? client).post({ +export const readWarehousesApiV1ErpWarehousesGet = (options?: Options) => (options?.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}/assign-to-role/{role_id}', + url: '/api/v1/erp/warehouses/', ...options }); /** - * 从角色解除权限绑定 - * - * 从角色解除权限绑定 - * - * 将指定权限从指定角色中移除。 - * - * Args: - * permission_id: 权限ID - * role_id: 角色ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 解绑成功响应消息 - * - * Raises: - * HTTPException: 当角色不存在时抛出404错误 + * Create Warehouse */ -export const unbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPost = (options: Options) => (options.client ?? client).post({ +export const createWarehouseApiV1ErpWarehousesPost = (options: Options) => (options.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}/unbind-from-role/{role_id}', - ...options -}); - -/** - * 将权限分配给用户 - * - * 将权限分配给用户 - * - * 将指定权限分配给指定用户。如果用户没有专属角色,则会自动创建一个专属角色。 - * - * Args: - * permission_id: 权限ID - * user_id: 用户UUID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 分配成功响应消息 - * - * Raises: - * HTTPException: 当用户或权限不存在时抛出404错误 - */ -export const assignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPost = (options: Options) => (options.client ?? client).post({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}/assign-to-user/{user_id}', - ...options -}); - -/** - * 从用户解除权限绑定 - * - * 从用户解除权限绑定 - * - * 将指定权限从指定用户中移除。 - * - * Args: - * permission_id: 权限ID - * user_id: 用户UUID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 解绑成功响应消息 - * - * Raises: - * HTTPException: 当用户或角色不存在时抛出404错误 - */ -export const unbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPost = (options: Options) => (options.client ?? client).post({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/permissions/{permission_id}/unbind-from-user/{user_id}', - ...options -}); - -/** - * 获取角色列表 - * - * 获取角色列表 - * - * 返回系统中的所有角色,支持分页。 - * - * Args: - * skip: 跳过的记录数,用于分页 - * limit: 返回的最大记录数,用于分页 - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * list[Role]: 角色对象列表 - */ -export const listRolesApiV1RolesGet = (options?: Options) => (options?.client ?? client).get({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles', - ...options -}); - -/** - * 创建角色 - * - * 创建角色 - * - * 创建新的角色,如 admin、manager、operator 等。 - * - * Args: - * name: 角色名称 - * description: 角色描述,可选 - * is_only_user: 是否仅用于单个用户,默认为False - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Role: 创建成功的角色对象 - * - * Raises: - * HTTPException: 当角色名称已存在时抛出400错误 - */ -export const createRoleApiV1RolesPost = (options: Options) => (options.client ?? client).post({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles', - ...options -}); - -/** - * 删除角色 - * - * 删除角色 - * - * 删除指定角色。如果角色已被分配给用户,则无法删除。 - * - * Args: - * role_id: 角色ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 删除成功响应消息 - * - * Raises: - * HTTPException: 当角色被用户使用时抛出400错误,角色不存在时抛出404错误 - */ -export const deleteRoleApiV1RolesRoleIdDelete = (options: Options) => (options.client ?? client).delete({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}', - ...options -}); - -/** - * 获取角色详情 - * - * 获取角色详情 - * - * 根据角色ID获取指定角色的详细信息。 - * - * Args: - * role_id: 角色ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Role: 角色对象 - * - * Raises: - * HTTPException: 当角色不存在时抛出404错误 - */ -export const getRoleApiV1RolesRoleIdGet = (options: Options) => (options.client ?? client).get({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}', - ...options -}); - -/** - * 更新角色 - * - * 更新角色信息 - * - * 更新指定角色的名称或描述等信息。 - * - * Args: - * role_id: 角色ID - * role_update: 角色更新数据,所有字段可选 - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * Role: 更新后的角色对象 - * - * Raises: - * HTTPException: 当角色不存在时抛出404错误 - */ -export const updateRoleApiV1RolesRoleIdPut = (options: Options) => (options.client ?? client).put({ - security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}', + url: '/api/v1/erp/warehouses/', ...options, headers: { 'Content-Type': 'application/json', @@ -676,121 +714,387 @@ export const updateRoleApiV1RolesRoleIdPut = (options: Options) => (options.client ?? client).post({ +export const deleteWarehouseApiV1ErpWarehousesWarehouseIdDelete = (options: Options) => (options.client ?? client).delete({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}/assign-to-user/{user_id}', + url: '/api/v1/erp/warehouses/{warehouse_id}', ...options }); /** - * 从用户解除角色绑定 - * - * 从用户解除角色绑定 - * - * 将指定角色从指定用户中移除。 - * - * Args: - * role_id: 角色ID - * user_id: 用户UUID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 解绑成功响应消息 - * - * Raises: - * HTTPException: 当用户不存在时抛出404错误 + * Read Warehouse */ -export const unbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPost = (options: Options) => (options.client ?? client).post({ +export const readWarehouseApiV1ErpWarehousesWarehouseIdGet = (options: Options) => (options.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}/unbind-from-user/{user_id}', + url: '/api/v1/erp/warehouses/{warehouse_id}', ...options }); /** - * 获取角色的权限列表 - * - * 获取指定角色的所有权限 - * - * Args: - * role_id: 角色ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * list: 权限对象列表 + * Update Warehouse */ -export const getRolePermissionsApiV1RolesRoleIdPermissionsGet = (options: Options) => (options.client ?? client).get({ +export const updateWarehouseApiV1ErpWarehousesWarehouseIdPatch = (options: Options) => (options.client ?? client).patch({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}/permissions', + url: '/api/v1/erp/warehouses/{warehouse_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Inventory + */ +export const readInventoryApiV1ErpInventoryGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/inventory/', ...options }); /** - * 为角色分配权限 - * - * 为角色分配权限 - * - * 将指定权限分配给指定角色。 - * - * Args: - * role_id: 角色ID - * permission_id: 权限ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 分配成功响应消息 - * - * Raises: - * HTTPException: 当权限已分配给角色或角色/权限不存在时抛出错误 + * Read Inventory By Product */ -export const assignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPost = (options: Options) => (options.client ?? client).post({ +export const readInventoryByProductApiV1ErpInventoryProductProductIdGet = (options: Options) => (options.client ?? client).get({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}/assign-permission/{permission_id}', + url: '/api/v1/erp/inventory/product/{product_id}', ...options }); /** - * 从角色解除权限绑定 - * - * 从角色解除权限绑定 - * - * 将指定权限从指定角色中移除。 - * - * Args: - * role_id: 角色ID - * permission_id: 权限ID - * session: 数据库会话对象 - * current_user: 当前登录用户 - * - * Returns: - * dict: 解绑成功响应消息 - * - * Raises: - * HTTPException: 当角色不存在时抛出404错误 + * Adjust Inventory */ -export const unbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPost = (options: Options) => (options.client ?? client).post({ +export const adjustInventoryApiV1ErpInventoryAdjustPost = (options: Options) => (options.client ?? client).post({ security: [{ scheme: 'bearer', type: 'http' }], - url: '/api/v1/roles/{role_id}/unbind-permission/{permission_id}', + url: '/api/v1/erp/inventory/adjust', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Movements + */ +export const readMovementsApiV1ErpInventoryMovementsGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/inventory/movements', + ...options +}); + +/** + * Read Suppliers + */ +export const readSuppliersApiV1ErpSuppliersGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/suppliers/', + ...options +}); + +/** + * Create Supplier + */ +export const createSupplierApiV1ErpSuppliersPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/suppliers/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Supplier + */ +export const deleteSupplierApiV1ErpSuppliersSupplierIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/suppliers/{supplier_id}', + ...options +}); + +/** + * Read Supplier + */ +export const readSupplierApiV1ErpSuppliersSupplierIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/suppliers/{supplier_id}', + ...options +}); + +/** + * Update Supplier + */ +export const updateSupplierApiV1ErpSuppliersSupplierIdPatch = (options: Options) => (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/suppliers/{supplier_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Customers + */ +export const readCustomersApiV1ErpCustomersGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/customers/', + ...options +}); + +/** + * Create Customer + */ +export const createCustomerApiV1ErpCustomersPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/customers/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Customer + */ +export const deleteCustomerApiV1ErpCustomersCustomerIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/customers/{customer_id}', + ...options +}); + +/** + * Read Customer + */ +export const readCustomerApiV1ErpCustomersCustomerIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/customers/{customer_id}', + ...options +}); + +/** + * Update Customer + */ +export const updateCustomerApiV1ErpCustomersCustomerIdPatch = (options: Options) => (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/customers/{customer_id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Purchase Orders + */ +export const readPurchaseOrdersApiV1ErpPurchaseOrdersGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-orders/', + ...options +}); + +/** + * Create Purchase Order + */ +export const createPurchaseOrderApiV1ErpPurchaseOrdersPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-orders/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Purchase Order + */ +export const deletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-orders/{order_id}', + ...options +}); + +/** + * Read Purchase Order + */ +export const readPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-orders/{order_id}', + ...options +}); + +/** + * Update Purchase Order Status + */ +export const updatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatch = (options: Options) => (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-orders/{order_id}/status', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Purchase Inbounds + */ +export const readPurchaseInboundsApiV1ErpPurchaseInboundsGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-inbounds/', + ...options +}); + +/** + * Create Purchase Inbound + */ +export const createPurchaseInboundApiV1ErpPurchaseInboundsPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-inbounds/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Purchase Inbound + */ +export const deletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-inbounds/{inbound_id}', + ...options +}); + +/** + * Read Purchase Inbound + */ +export const readPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-inbounds/{inbound_id}', + ...options +}); + +/** + * Approve Purchase Inbound + * + * 审核入库单 - 同时更新库存 + */ +export const approvePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/purchase-inbounds/{inbound_id}/approve', + ...options +}); + +/** + * Read Sales Orders + */ +export const readSalesOrdersApiV1ErpSalesOrdersGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-orders/', + ...options +}); + +/** + * Create Sales Order + */ +export const createSalesOrderApiV1ErpSalesOrdersPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-orders/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Sales Order + */ +export const deleteSalesOrderApiV1ErpSalesOrdersOrderIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-orders/{order_id}', + ...options +}); + +/** + * Read Sales Order + */ +export const readSalesOrderApiV1ErpSalesOrdersOrderIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-orders/{order_id}', + ...options +}); + +/** + * Update Sales Order Status + */ +export const updateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatch = (options: Options) => (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-orders/{order_id}/status', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Read Sales Outbounds + */ +export const readSalesOutboundsApiV1ErpSalesOutboundsGet = (options?: Options) => (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-outbounds/', + ...options +}); + +/** + * Create Sales Outbound + */ +export const createSalesOutboundApiV1ErpSalesOutboundsPost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-outbounds/', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Delete Sales Outbound + */ +export const deleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDelete = (options: Options) => (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-outbounds/{outbound_id}', + ...options +}); + +/** + * Read Sales Outbound + */ +export const readSalesOutboundApiV1ErpSalesOutboundsOutboundIdGet = (options: Options) => (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-outbounds/{outbound_id}', + ...options +}); + +/** + * Approve Sales Outbound + * + * 审核出库单 - 同时扣减库存 + */ +export const approveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePost = (options: Options) => (options.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/v1/erp/sales-outbounds/{outbound_id}/approve', ...options }); diff --git a/src/chongming-web/src/api/generated/types.gen.ts b/src/chongming-web/src/api/generated/types.gen.ts index e63891a..37c63ec 100644 --- a/src/chongming-web/src/api/generated/types.gen.ts +++ b/src/chongming-web/src/api/generated/types.gen.ts @@ -5,15 +5,139 @@ export type ClientOptions = { }; /** - * Body_upload_image_api_v1_upload_image_post + * Body_upload_image_api_v1_system_upload_image_post */ -export type BodyUploadImageApiV1UploadImagePost = { +export type BodyUploadImageApiV1SystemUploadImagePost = { /** * File */ file: Blob | File; }; +/** + * CustomerCreate + * + * 创建客户请求模型 + */ +export type CustomerCreate = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Contact Person + */ + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * CustomerRead + * + * 客户响应模型 + */ +export type CustomerRead = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Contact Person + */ + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Remark + */ + remark?: string | null; + /** + * Id + */ + id: string; +}; + +/** + * CustomerUpdate + * + * 更新客户请求模型 + */ +export type CustomerUpdate = { + /** + * Code + */ + code?: string | null; + /** + * Name + */ + name?: string | null; + /** + * Contact Person + */ + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean | null; + /** + * Remark + */ + remark?: string | null; +}; + /** * Detail * @@ -61,6 +185,124 @@ export type HttpValidationError = { detail?: Array; }; +/** + * InventoryAdjustRequest + */ +export type InventoryAdjustRequest = { + /** + * Product Id + */ + product_id: string; + /** + * Warehouse Id + */ + warehouse_id: string; + /** + * Quantity + */ + quantity: number; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * InventoryMovementRead + * + * 库存变动响应模型 + */ +export type InventoryMovementRead = { + /** + * Id + */ + id: string; + /** + * Product Id + */ + product_id: string; + /** + * Warehouse Id + */ + warehouse_id: string; + /** + * Change Type + */ + change_type: string; + /** + * Order Type + */ + order_type: string; + /** + * Order No + */ + order_no: string; + /** + * Quantity + */ + quantity: number; + /** + * Before Quantity + */ + before_quantity: number; + /** + * After Quantity + */ + after_quantity: number; + /** + * Remark + */ + remark: string | null; + /** + * Operator + */ + operator: string | null; + /** + * Created At + */ + created_at: string; +}; + +/** + * InventoryRead + * + * 库存响应模型 + */ +export type InventoryRead = { + /** + * Id + */ + id: string; + /** + * Product Id + */ + product_id: string; + /** + * Warehouse Id + */ + warehouse_id: string; + /** + * Quantity + */ + quantity: number; + /** + * Locked Quantity + */ + locked_quantity: number; + /** + * Available Quantity + */ + available_quantity: number; + /** + * Created At + */ + created_at: string; + /** + * Updated At + */ + updated_at: string; +}; + /** * Permission * @@ -163,6 +405,262 @@ export type PermissionUpdate = { action?: string | null; }; +/** + * ProductCreate + * + * 创建产品请求模型 + */ +export type ProductCreate = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Category + */ + category?: string | null; + /** + * Spec + */ + spec?: string | null; + /** + * Unit + */ + unit?: string; + /** + * Purchase Price + */ + purchase_price?: number; + /** + * Sale Price + */ + sale_price?: number; + /** + * Description + */ + description?: string | null; + /** + * Is Active + */ + is_active?: boolean; +}; + +/** + * ProductRead + * + * 产品响应模型 + */ +export type ProductRead = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Category + */ + category?: string | null; + /** + * Spec + */ + spec?: string | null; + /** + * Unit + */ + unit?: string; + /** + * Purchase Price + */ + purchase_price?: number; + /** + * Sale Price + */ + sale_price?: number; + /** + * Description + */ + description?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Id + */ + id: string; +}; + +/** + * ProductUpdate + * + * 更新产品请求模型 + */ +export type ProductUpdate = { + /** + * Code + */ + code?: string | null; + /** + * Name + */ + name?: string | null; + /** + * Category + */ + category?: string | null; + /** + * Spec + */ + spec?: string | null; + /** + * Unit + */ + unit?: string | null; + /** + * Purchase Price + */ + purchase_price?: number | null; + /** + * Sale Price + */ + sale_price?: number | null; + /** + * Description + */ + description?: string | null; + /** + * Is Active + */ + is_active?: boolean | null; +}; + +/** + * PurchaseInboundCreate + * + * 创建采购入库单请求模型 + */ +export type PurchaseInboundCreate = { + /** + * Inbound No + */ + inbound_no: string; + /** + * Purchase Order Id + */ + purchase_order_id: string; + /** + * Warehouse Id + */ + warehouse_id: string; + /** + * Remark + */ + remark?: string | null; + /** + * Items + */ + items: Array; +}; + +/** + * PurchaseInboundItemCreate + * + * 创建入库明细请求模型 + */ +export type PurchaseInboundItemCreate = { + /** + * Product Id + */ + product_id: string; + /** + * Quantity + */ + quantity: number; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * PurchaseOrderCreate + * + * 创建采购订单请求模型 + */ +export type PurchaseOrderCreate = { + /** + * Order No + */ + order_no: string; + /** + * Supplier Id + */ + supplier_id: string; + /** + * Remark + */ + remark?: string | null; + /** + * Items + */ + items: Array; +}; + +/** + * PurchaseOrderItemCreate + * + * 创建采购订单明细请求模型 + */ +export type PurchaseOrderItemCreate = { + /** + * Product Id + */ + product_id: string; + /** + * Quantity + */ + quantity: number; + /** + * Price + */ + price: number; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * PurchaseOrderUpdate + * + * 更新采购订单请求模型 + */ +export type PurchaseOrderUpdate = { + /** + * Status + */ + status?: string | null; + /** + * Remark + */ + remark?: string | null; + /** + * Total Amount + */ + total_amount?: number | null; + /** + * Approved By + */ + approved_by?: string | null; +}; + /** * RefreshTokenResponse * @@ -239,84 +737,247 @@ export type RoleUpdate = { }; /** - * TodoCreate + * SalesOrderCreate * - * 创建Todo任务时使用的请求模型类。 - * - * 继承自TodoBase,用于接收前端传入的新建任务数据。 - * 不包含id字段,因为id由数据库自动生成。 + * 创建销售订单请求模型 */ -export type TodoCreate = { +export type SalesOrderCreate = { /** - * Title + * Order No */ - title: string; + order_no: string; /** - * Description + * Customer Id */ - description?: string | null; + customer_id: string; /** - * Completed + * Remark */ - completed?: boolean; + remark?: string | null; + /** + * Items + */ + items: Array; }; /** - * TodoRead + * SalesOrderItemCreate * - * 读取Todo任务时使用的响应模型类。 - * - * 继承自TodoBase,用于向客户端返回任务数据。 - * 包含完整的任务信息,包括自动生成的id字段。 - * - * Attributes: - * id: 任务唯一标识符,整数类型,必填字段 + * 创建销售订单明细请求模型 */ -export type TodoRead = { +export type SalesOrderItemCreate = { /** - * Title + * Product Id */ - title: string; + product_id: string; /** - * Description + * Quantity */ - description?: string | null; + quantity: number; /** - * Completed + * Price */ - completed?: boolean; + price: number; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * SalesOrderUpdate + * + * 更新销售订单请求模型 + */ +export type SalesOrderUpdate = { + /** + * Status + */ + status?: string | null; + /** + * Remark + */ + remark?: string | null; + /** + * Total Amount + */ + total_amount?: number | null; + /** + * Approved By + */ + approved_by?: string | null; +}; + +/** + * SalesOutboundCreate + * + * 创建销售出库单请求模型 + */ +export type SalesOutboundCreate = { + /** + * Outbound No + */ + outbound_no: string; + /** + * Sales Order Id + */ + sales_order_id: string; + /** + * Warehouse Id + */ + warehouse_id: string; + /** + * Remark + */ + remark?: string | null; + /** + * Items + */ + items: Array; +}; + +/** + * SalesOutboundItemCreate + * + * 创建出库明细请求模型 + */ +export type SalesOutboundItemCreate = { + /** + * Product Id + */ + product_id: string; + /** + * Quantity + */ + quantity: number; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * SupplierCreate + * + * 创建供应商请求模型 + */ +export type SupplierCreate = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Contact Person + */ + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Remark + */ + remark?: string | null; +}; + +/** + * SupplierRead + * + * 供应商响应模型 + */ +export type SupplierRead = { + /** + * Code + */ + code: string; + /** + * Name + */ + name: string; + /** + * Contact Person + */ + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Remark + */ + remark?: string | null; /** * Id */ - id: number; + id: string; }; /** - * TodoUpdate + * SupplierUpdate * - * 更新Todo任务时使用的请求模型类。 - * - * 所有字段均为可选,允许部分更新任务信息。 - * 只有提供的字段会被更新,未提供的字段保持原值。 - * - * Attributes: - * title: 任务标题,可选字段 - * description: 任务描述,可选字段 - * completed: 任务完成状态,可选字段 + * 更新供应商请求模型 */ -export type TodoUpdate = { +export type SupplierUpdate = { /** - * Title + * Code */ - title?: string | null; + code?: string | null; /** - * Description + * Name */ - description?: string | null; + name?: string | null; /** - * Completed + * Contact Person */ - completed?: boolean | null; + contact_person?: string | null; + /** + * Phone + */ + phone?: string | null; + /** + * Email + */ + email?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Is Active + */ + is_active?: boolean | null; + /** + * Remark + */ + remark?: string | null; }; /** @@ -525,163 +1186,114 @@ export type ValidationError = { }; }; -export type ReadTodosApiV1TodosGetData = { - body?: never; - path?: never; - query?: { - /** - * Offset - */ - offset?: number; - /** - * Limit - */ - limit?: number; - }; - url: '/api/v1/todos/'; -}; - -export type ReadTodosApiV1TodosGetErrors = { +/** + * WarehouseCreate + * + * 创建仓库请求模型 + */ +export type WarehouseCreate = { /** - * Validation Error + * Code */ - 422: HttpValidationError; -}; - -export type ReadTodosApiV1TodosGetError = ReadTodosApiV1TodosGetErrors[keyof ReadTodosApiV1TodosGetErrors]; - -export type ReadTodosApiV1TodosGetResponses = { + code: string; /** - * Response Read Todos Api V1 Todos Get - * - * Successful Response + * Name */ - 200: Array; -}; - -export type ReadTodosApiV1TodosGetResponse = ReadTodosApiV1TodosGetResponses[keyof ReadTodosApiV1TodosGetResponses]; - -export type CreateTodoApiV1TodosPostData = { - body: TodoCreate; - path?: never; - query?: never; - url: '/api/v1/todos/'; -}; - -export type CreateTodoApiV1TodosPostErrors = { + name: string; /** - * Validation Error + * Address */ - 422: HttpValidationError; -}; - -export type CreateTodoApiV1TodosPostError = CreateTodoApiV1TodosPostErrors[keyof CreateTodoApiV1TodosPostErrors]; - -export type CreateTodoApiV1TodosPostResponses = { + address?: string | null; /** - * Successful Response + * Manager */ - 200: TodoRead; -}; - -export type CreateTodoApiV1TodosPostResponse = CreateTodoApiV1TodosPostResponses[keyof CreateTodoApiV1TodosPostResponses]; - -export type DeleteTodoApiV1TodosTodoIdDeleteData = { - body?: never; - path: { - /** - * Todo Id - */ - todo_id: number; - }; - query?: never; - url: '/api/v1/todos/{todo_id}'; -}; - -export type DeleteTodoApiV1TodosTodoIdDeleteErrors = { + manager?: string | null; /** - * Validation Error + * Is Active */ - 422: HttpValidationError; -}; - -export type DeleteTodoApiV1TodosTodoIdDeleteError = DeleteTodoApiV1TodosTodoIdDeleteErrors[keyof DeleteTodoApiV1TodosTodoIdDeleteErrors]; - -export type DeleteTodoApiV1TodosTodoIdDeleteResponses = { + is_active?: boolean; /** - * Successful Response + * Description */ - 200: unknown; + description?: string | null; }; -export type ReadTodoApiV1TodosTodoIdGetData = { - body?: never; - path: { - /** - * Todo Id - */ - todo_id: number; - }; - query?: never; - url: '/api/v1/todos/{todo_id}'; -}; - -export type ReadTodoApiV1TodosTodoIdGetErrors = { +/** + * WarehouseRead + * + * 仓库响应模型 + */ +export type WarehouseRead = { /** - * Validation Error + * Code */ - 422: HttpValidationError; -}; - -export type ReadTodoApiV1TodosTodoIdGetError = ReadTodoApiV1TodosTodoIdGetErrors[keyof ReadTodoApiV1TodosTodoIdGetErrors]; - -export type ReadTodoApiV1TodosTodoIdGetResponses = { + code: string; /** - * Successful Response + * Name */ - 200: TodoRead; -}; - -export type ReadTodoApiV1TodosTodoIdGetResponse = ReadTodoApiV1TodosTodoIdGetResponses[keyof ReadTodoApiV1TodosTodoIdGetResponses]; - -export type UpdateTodoApiV1TodosTodoIdPatchData = { - body: TodoUpdate; - path: { - /** - * Todo Id - */ - todo_id: number; - }; - query?: never; - url: '/api/v1/todos/{todo_id}'; -}; - -export type UpdateTodoApiV1TodosTodoIdPatchErrors = { + name: string; /** - * Validation Error + * Address */ - 422: HttpValidationError; -}; - -export type UpdateTodoApiV1TodosTodoIdPatchError = UpdateTodoApiV1TodosTodoIdPatchErrors[keyof UpdateTodoApiV1TodosTodoIdPatchErrors]; - -export type UpdateTodoApiV1TodosTodoIdPatchResponses = { + address?: string | null; /** - * Successful Response + * Manager */ - 200: TodoRead; + manager?: string | null; + /** + * Is Active + */ + is_active?: boolean; + /** + * Description + */ + description?: string | null; + /** + * Id + */ + id: string; }; -export type UpdateTodoApiV1TodosTodoIdPatchResponse = UpdateTodoApiV1TodosTodoIdPatchResponses[keyof UpdateTodoApiV1TodosTodoIdPatchResponses]; +/** + * WarehouseUpdate + * + * 更新仓库请求模型 + */ +export type WarehouseUpdate = { + /** + * Code + */ + code?: string | null; + /** + * Name + */ + name?: string | null; + /** + * Address + */ + address?: string | null; + /** + * Manager + */ + manager?: string | null; + /** + * Is Active + */ + is_active?: boolean | null; + /** + * Description + */ + description?: string | null; +}; -export type RegisterApiV1AuthRegisterPostData = { +export type RegisterApiV1SystemAuthRegisterPostData = { body: UserCreate; path?: never; query?: never; - url: '/api/v1/auth/register'; + url: '/api/v1/system/auth/register'; }; -export type RegisterApiV1AuthRegisterPostErrors = { +export type RegisterApiV1SystemAuthRegisterPostErrors = { /** * 邮箱已被注册 */ @@ -692,25 +1304,25 @@ export type RegisterApiV1AuthRegisterPostErrors = { 422: HttpValidationError; }; -export type RegisterApiV1AuthRegisterPostError = RegisterApiV1AuthRegisterPostErrors[keyof RegisterApiV1AuthRegisterPostErrors]; +export type RegisterApiV1SystemAuthRegisterPostError = RegisterApiV1SystemAuthRegisterPostErrors[keyof RegisterApiV1SystemAuthRegisterPostErrors]; -export type RegisterApiV1AuthRegisterPostResponses = { +export type RegisterApiV1SystemAuthRegisterPostResponses = { /** * Successful Response */ 200: UserRead; }; -export type RegisterApiV1AuthRegisterPostResponse = RegisterApiV1AuthRegisterPostResponses[keyof RegisterApiV1AuthRegisterPostResponses]; +export type RegisterApiV1SystemAuthRegisterPostResponse = RegisterApiV1SystemAuthRegisterPostResponses[keyof RegisterApiV1SystemAuthRegisterPostResponses]; -export type LoginApiV1AuthLoginPostData = { +export type LoginApiV1SystemAuthLoginPostData = { body: UserLogin; path?: never; query?: never; - url: '/api/v1/auth/login'; + url: '/api/v1/system/auth/login'; }; -export type LoginApiV1AuthLoginPostErrors = { +export type LoginApiV1SystemAuthLoginPostErrors = { /** * 邮箱不存在 */ @@ -729,139 +1341,114 @@ export type LoginApiV1AuthLoginPostErrors = { 422: HttpValidationError; }; -export type LoginApiV1AuthLoginPostError = LoginApiV1AuthLoginPostErrors[keyof LoginApiV1AuthLoginPostErrors]; +export type LoginApiV1SystemAuthLoginPostError = LoginApiV1SystemAuthLoginPostErrors[keyof LoginApiV1SystemAuthLoginPostErrors]; -export type LoginApiV1AuthLoginPostResponses = { +export type LoginApiV1SystemAuthLoginPostResponses = { /** * Successful Response */ 200: TokenResponse; }; -export type LoginApiV1AuthLoginPostResponse = LoginApiV1AuthLoginPostResponses[keyof LoginApiV1AuthLoginPostResponses]; +export type LoginApiV1SystemAuthLoginPostResponse = LoginApiV1SystemAuthLoginPostResponses[keyof LoginApiV1SystemAuthLoginPostResponses]; -export type RefreshTokenApiV1AuthRefreshPostData = { +export type RefreshTokenApiV1SystemAuthRefreshPostData = { body?: never; path?: never; query?: never; - url: '/api/v1/auth/refresh'; + url: '/api/v1/system/auth/refresh'; }; -export type RefreshTokenApiV1AuthRefreshPostErrors = { +export type RefreshTokenApiV1SystemAuthRefreshPostErrors = { /** * 无效的刷新令牌 */ 401: HttpError; }; -export type RefreshTokenApiV1AuthRefreshPostError = RefreshTokenApiV1AuthRefreshPostErrors[keyof RefreshTokenApiV1AuthRefreshPostErrors]; +export type RefreshTokenApiV1SystemAuthRefreshPostError = RefreshTokenApiV1SystemAuthRefreshPostErrors[keyof RefreshTokenApiV1SystemAuthRefreshPostErrors]; -export type RefreshTokenApiV1AuthRefreshPostResponses = { +export type RefreshTokenApiV1SystemAuthRefreshPostResponses = { /** * Successful Response */ 200: RefreshTokenResponse; }; -export type RefreshTokenApiV1AuthRefreshPostResponse = RefreshTokenApiV1AuthRefreshPostResponses[keyof RefreshTokenApiV1AuthRefreshPostResponses]; +export type RefreshTokenApiV1SystemAuthRefreshPostResponse = RefreshTokenApiV1SystemAuthRefreshPostResponses[keyof RefreshTokenApiV1SystemAuthRefreshPostResponses]; -export type LogoutApiV1AuthLogoutPostData = { +export type LogoutApiV1SystemAuthLogoutPostData = { body?: never; path?: never; query?: never; - url: '/api/v1/auth/logout'; + url: '/api/v1/system/auth/logout'; }; -export type LogoutApiV1AuthLogoutPostResponses = { +export type LogoutApiV1SystemAuthLogoutPostResponses = { /** * Successful Response */ 200: Userlogout; }; -export type LogoutApiV1AuthLogoutPostResponse = LogoutApiV1AuthLogoutPostResponses[keyof LogoutApiV1AuthLogoutPostResponses]; +export type LogoutApiV1SystemAuthLogoutPostResponse = LogoutApiV1SystemAuthLogoutPostResponses[keyof LogoutApiV1SystemAuthLogoutPostResponses]; -export type LogoutAllApiV1AuthLogoutAllPostData = { +export type LogoutAllApiV1SystemAuthLogoutAllPostData = { body?: never; path?: never; query?: never; - url: '/api/v1/auth/logout-all'; + url: '/api/v1/system/auth/logout-all'; }; -export type LogoutAllApiV1AuthLogoutAllPostResponses = { +export type LogoutAllApiV1SystemAuthLogoutAllPostResponses = { /** * Successful Response */ 200: Userlogout; }; -export type LogoutAllApiV1AuthLogoutAllPostResponse = LogoutAllApiV1AuthLogoutAllPostResponses[keyof LogoutAllApiV1AuthLogoutAllPostResponses]; +export type LogoutAllApiV1SystemAuthLogoutAllPostResponse = LogoutAllApiV1SystemAuthLogoutAllPostResponses[keyof LogoutAllApiV1SystemAuthLogoutAllPostResponses]; -export type GetSessionsApiV1AuthSessionsGetData = { +export type GetSessionsApiV1SystemAuthSessionsGetData = { body?: never; path?: never; query?: never; - url: '/api/v1/auth/sessions'; + url: '/api/v1/system/auth/sessions'; }; -export type GetSessionsApiV1AuthSessionsGetResponses = { +export type GetSessionsApiV1SystemAuthSessionsGetResponses = { /** * Successful Response */ 200: unknown; }; -export type GetCurrentUserInfoApiV1AuthMeGetData = { +export type GetCurrentUserInfoApiV1SystemAuthMeGetData = { body?: never; path?: never; query?: never; - url: '/api/v1/auth/me'; + url: '/api/v1/system/auth/me'; }; -export type GetCurrentUserInfoApiV1AuthMeGetErrors = { +export type GetCurrentUserInfoApiV1SystemAuthMeGetErrors = { /** * 用户不存在 */ 404: HttpError; }; -export type GetCurrentUserInfoApiV1AuthMeGetError = GetCurrentUserInfoApiV1AuthMeGetErrors[keyof GetCurrentUserInfoApiV1AuthMeGetErrors]; +export type GetCurrentUserInfoApiV1SystemAuthMeGetError = GetCurrentUserInfoApiV1SystemAuthMeGetErrors[keyof GetCurrentUserInfoApiV1SystemAuthMeGetErrors]; -export type GetCurrentUserInfoApiV1AuthMeGetResponses = { +export type GetCurrentUserInfoApiV1SystemAuthMeGetResponses = { /** * Successful Response */ 200: UserRead; }; -export type GetCurrentUserInfoApiV1AuthMeGetResponse = GetCurrentUserInfoApiV1AuthMeGetResponses[keyof GetCurrentUserInfoApiV1AuthMeGetResponses]; +export type GetCurrentUserInfoApiV1SystemAuthMeGetResponse = GetCurrentUserInfoApiV1SystemAuthMeGetResponses[keyof GetCurrentUserInfoApiV1SystemAuthMeGetResponses]; -export type UploadImageApiV1UploadImagePostData = { - body: BodyUploadImageApiV1UploadImagePost; - path?: never; - query?: never; - url: '/api/v1/upload/image'; -}; - -export type UploadImageApiV1UploadImagePostErrors = { - /** - * Validation Error - */ - 422: HttpValidationError; -}; - -export type UploadImageApiV1UploadImagePostError = UploadImageApiV1UploadImagePostErrors[keyof UploadImageApiV1UploadImagePostErrors]; - -export type UploadImageApiV1UploadImagePostResponses = { - /** - * Successful Response - */ - 200: UploadResponse; -}; - -export type UploadImageApiV1UploadImagePostResponse = UploadImageApiV1UploadImagePostResponses[keyof UploadImageApiV1UploadImagePostResponses]; - -export type ListPermissionsApiV1PermissionsGetData = { +export type ListPermissionsApiV1SystemPermissionGetData = { body?: never; path?: never; query?: { @@ -878,37 +1465,37 @@ export type ListPermissionsApiV1PermissionsGetData = { */ resource?: string | null; }; - url: '/api/v1/permissions'; + url: '/api/v1/system/permission'; }; -export type ListPermissionsApiV1PermissionsGetErrors = { +export type ListPermissionsApiV1SystemPermissionGetErrors = { /** * Validation Error */ 422: HttpValidationError; }; -export type ListPermissionsApiV1PermissionsGetError = ListPermissionsApiV1PermissionsGetErrors[keyof ListPermissionsApiV1PermissionsGetErrors]; +export type ListPermissionsApiV1SystemPermissionGetError = ListPermissionsApiV1SystemPermissionGetErrors[keyof ListPermissionsApiV1SystemPermissionGetErrors]; -export type ListPermissionsApiV1PermissionsGetResponses = { +export type ListPermissionsApiV1SystemPermissionGetResponses = { /** - * Response List Permissions Api V1 Permissions Get + * Response List Permissions Api V1 System Permission Get * * Successful Response */ 200: Array; }; -export type ListPermissionsApiV1PermissionsGetResponse = ListPermissionsApiV1PermissionsGetResponses[keyof ListPermissionsApiV1PermissionsGetResponses]; +export type ListPermissionsApiV1SystemPermissionGetResponse = ListPermissionsApiV1SystemPermissionGetResponses[keyof ListPermissionsApiV1SystemPermissionGetResponses]; -export type CreatePermissionApiV1PermissionsPostData = { +export type CreatePermissionApiV1SystemPermissionPostData = { body: PermissionCreate; path?: never; query?: never; - url: '/api/v1/permissions'; + url: '/api/v1/system/permission'; }; -export type CreatePermissionApiV1PermissionsPostErrors = { +export type CreatePermissionApiV1SystemPermissionPostErrors = { /** * 权限名称已存在 */ @@ -919,18 +1506,18 @@ export type CreatePermissionApiV1PermissionsPostErrors = { 422: HttpValidationError; }; -export type CreatePermissionApiV1PermissionsPostError = CreatePermissionApiV1PermissionsPostErrors[keyof CreatePermissionApiV1PermissionsPostErrors]; +export type CreatePermissionApiV1SystemPermissionPostError = CreatePermissionApiV1SystemPermissionPostErrors[keyof CreatePermissionApiV1SystemPermissionPostErrors]; -export type CreatePermissionApiV1PermissionsPostResponses = { +export type CreatePermissionApiV1SystemPermissionPostResponses = { /** * Successful Response */ 200: Permission; }; -export type CreatePermissionApiV1PermissionsPostResponse = CreatePermissionApiV1PermissionsPostResponses[keyof CreatePermissionApiV1PermissionsPostResponses]; +export type CreatePermissionApiV1SystemPermissionPostResponse = CreatePermissionApiV1SystemPermissionPostResponses[keyof CreatePermissionApiV1SystemPermissionPostResponses]; -export type DeletePermissionApiV1PermissionsPermissionIdDeleteData = { +export type DeletePermissionApiV1SystemPermissionPermissionIdDeleteData = { body?: never; path: { /** @@ -939,10 +1526,10 @@ export type DeletePermissionApiV1PermissionsPermissionIdDeleteData = { permission_id: number; }; query?: never; - url: '/api/v1/permissions/{permission_id}'; + url: '/api/v1/system/permission/{permission_id}'; }; -export type DeletePermissionApiV1PermissionsPermissionIdDeleteErrors = { +export type DeletePermissionApiV1SystemPermissionPermissionIdDeleteErrors = { /** * 权限已被角色使用,无法删除 */ @@ -957,16 +1544,16 @@ export type DeletePermissionApiV1PermissionsPermissionIdDeleteErrors = { 422: HttpValidationError; }; -export type DeletePermissionApiV1PermissionsPermissionIdDeleteError = DeletePermissionApiV1PermissionsPermissionIdDeleteErrors[keyof DeletePermissionApiV1PermissionsPermissionIdDeleteErrors]; +export type DeletePermissionApiV1SystemPermissionPermissionIdDeleteError = DeletePermissionApiV1SystemPermissionPermissionIdDeleteErrors[keyof DeletePermissionApiV1SystemPermissionPermissionIdDeleteErrors]; -export type DeletePermissionApiV1PermissionsPermissionIdDeleteResponses = { +export type DeletePermissionApiV1SystemPermissionPermissionIdDeleteResponses = { /** * Successful Response */ 200: unknown; }; -export type GetPermissionApiV1PermissionsPermissionIdGetData = { +export type GetPermissionApiV1SystemPermissionPermissionIdGetData = { body?: never; path: { /** @@ -975,10 +1562,10 @@ export type GetPermissionApiV1PermissionsPermissionIdGetData = { permission_id: number; }; query?: never; - url: '/api/v1/permissions/{permission_id}'; + url: '/api/v1/system/permission/{permission_id}'; }; -export type GetPermissionApiV1PermissionsPermissionIdGetErrors = { +export type GetPermissionApiV1SystemPermissionPermissionIdGetErrors = { /** * 权限不存在 */ @@ -989,18 +1576,18 @@ export type GetPermissionApiV1PermissionsPermissionIdGetErrors = { 422: HttpValidationError; }; -export type GetPermissionApiV1PermissionsPermissionIdGetError = GetPermissionApiV1PermissionsPermissionIdGetErrors[keyof GetPermissionApiV1PermissionsPermissionIdGetErrors]; +export type GetPermissionApiV1SystemPermissionPermissionIdGetError = GetPermissionApiV1SystemPermissionPermissionIdGetErrors[keyof GetPermissionApiV1SystemPermissionPermissionIdGetErrors]; -export type GetPermissionApiV1PermissionsPermissionIdGetResponses = { +export type GetPermissionApiV1SystemPermissionPermissionIdGetResponses = { /** * Successful Response */ 200: Permission; }; -export type GetPermissionApiV1PermissionsPermissionIdGetResponse = GetPermissionApiV1PermissionsPermissionIdGetResponses[keyof GetPermissionApiV1PermissionsPermissionIdGetResponses]; +export type GetPermissionApiV1SystemPermissionPermissionIdGetResponse = GetPermissionApiV1SystemPermissionPermissionIdGetResponses[keyof GetPermissionApiV1SystemPermissionPermissionIdGetResponses]; -export type UpdatePermissionApiV1PermissionsPermissionIdPutData = { +export type UpdatePermissionApiV1SystemPermissionPermissionIdPutData = { body: PermissionUpdate; path: { /** @@ -1009,10 +1596,10 @@ export type UpdatePermissionApiV1PermissionsPermissionIdPutData = { permission_id: number; }; query?: never; - url: '/api/v1/permissions/{permission_id}'; + url: '/api/v1/system/permission/{permission_id}'; }; -export type UpdatePermissionApiV1PermissionsPermissionIdPutErrors = { +export type UpdatePermissionApiV1SystemPermissionPermissionIdPutErrors = { /** * 权限不存在 */ @@ -1023,18 +1610,18 @@ export type UpdatePermissionApiV1PermissionsPermissionIdPutErrors = { 422: HttpValidationError; }; -export type UpdatePermissionApiV1PermissionsPermissionIdPutError = UpdatePermissionApiV1PermissionsPermissionIdPutErrors[keyof UpdatePermissionApiV1PermissionsPermissionIdPutErrors]; +export type UpdatePermissionApiV1SystemPermissionPermissionIdPutError = UpdatePermissionApiV1SystemPermissionPermissionIdPutErrors[keyof UpdatePermissionApiV1SystemPermissionPermissionIdPutErrors]; -export type UpdatePermissionApiV1PermissionsPermissionIdPutResponses = { +export type UpdatePermissionApiV1SystemPermissionPermissionIdPutResponses = { /** * Successful Response */ 200: Permission; }; -export type UpdatePermissionApiV1PermissionsPermissionIdPutResponse = UpdatePermissionApiV1PermissionsPermissionIdPutResponses[keyof UpdatePermissionApiV1PermissionsPermissionIdPutResponses]; +export type UpdatePermissionApiV1SystemPermissionPermissionIdPutResponse = UpdatePermissionApiV1SystemPermissionPermissionIdPutResponses[keyof UpdatePermissionApiV1SystemPermissionPermissionIdPutResponses]; -export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostData = { +export type AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostData = { body?: never; path: { /** @@ -1047,10 +1634,10 @@ export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleId role_id: number; }; query?: never; - url: '/api/v1/permissions/{permission_id}/assign-to-role/{role_id}'; + url: '/api/v1/system/permission/{permission_id}/assign-to-role/{role_id}'; }; -export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors = { +export type AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostErrors = { /** * 权限已分配给角色 */ @@ -1065,16 +1652,16 @@ export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleId 422: HttpValidationError; }; -export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostError = AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors[keyof AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors]; +export type AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostError = AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostErrors[keyof AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostErrors]; -export type AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostResponses = { +export type AssignPermissionToRoleApiV1SystemPermissionPermissionIdAssignToRoleRoleIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostData = { +export type UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostData = { body?: never; path: { /** @@ -1087,10 +1674,10 @@ export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRo role_id: number; }; query?: never; - url: '/api/v1/permissions/{permission_id}/unbind-from-role/{role_id}'; + url: '/api/v1/system/permission/{permission_id}/unbind-from-role/{role_id}'; }; -export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors = { +export type UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostErrors = { /** * 角色不存在 */ @@ -1101,16 +1688,16 @@ export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRo 422: HttpValidationError; }; -export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostError = UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors[keyof UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors]; +export type UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostError = UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostErrors[keyof UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostErrors]; -export type UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostResponses = { +export type UnbindPermissionFromRoleApiV1SystemPermissionPermissionIdUnbindFromRoleRoleIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostData = { +export type AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostData = { body?: never; path: { /** @@ -1123,10 +1710,10 @@ export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserId user_id: string; }; query?: never; - url: '/api/v1/permissions/{permission_id}/assign-to-user/{user_id}'; + url: '/api/v1/system/permission/{permission_id}/assign-to-user/{user_id}'; }; -export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors = { +export type AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostErrors = { /** * 用户或权限不存在 */ @@ -1137,16 +1724,16 @@ export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserId 422: HttpValidationError; }; -export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostError = AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors[keyof AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors]; +export type AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostError = AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostErrors[keyof AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostErrors]; -export type AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostResponses = { +export type AssignPermissionToUserApiV1SystemPermissionPermissionIdAssignToUserUserIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostData = { +export type UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostData = { body?: never; path: { /** @@ -1159,10 +1746,10 @@ export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUs user_id: string; }; query?: never; - url: '/api/v1/permissions/{permission_id}/unbind-from-user/{user_id}'; + url: '/api/v1/system/permission/{permission_id}/unbind-from-user/{user_id}'; }; -export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors = { +export type UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostErrors = { /** * 用户或权限不存在 */ @@ -1173,16 +1760,16 @@ export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUs 422: HttpValidationError; }; -export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostError = UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors[keyof UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors]; +export type UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostError = UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostErrors[keyof UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostErrors]; -export type UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostResponses = { +export type UnbindPermissionFromUserApiV1SystemPermissionPermissionIdUnbindFromUserUserIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type ListRolesApiV1RolesGetData = { +export type ListRolesApiV1SystemRoleGetData = { body?: never; path?: never; query?: { @@ -1195,30 +1782,30 @@ export type ListRolesApiV1RolesGetData = { */ limit?: number; }; - url: '/api/v1/roles'; + url: '/api/v1/system/role'; }; -export type ListRolesApiV1RolesGetErrors = { +export type ListRolesApiV1SystemRoleGetErrors = { /** * Validation Error */ 422: HttpValidationError; }; -export type ListRolesApiV1RolesGetError = ListRolesApiV1RolesGetErrors[keyof ListRolesApiV1RolesGetErrors]; +export type ListRolesApiV1SystemRoleGetError = ListRolesApiV1SystemRoleGetErrors[keyof ListRolesApiV1SystemRoleGetErrors]; -export type ListRolesApiV1RolesGetResponses = { +export type ListRolesApiV1SystemRoleGetResponses = { /** - * Response List Roles Api V1 Roles Get + * Response List Roles Api V1 System Role Get * * Successful Response */ 200: Array; }; -export type ListRolesApiV1RolesGetResponse = ListRolesApiV1RolesGetResponses[keyof ListRolesApiV1RolesGetResponses]; +export type ListRolesApiV1SystemRoleGetResponse = ListRolesApiV1SystemRoleGetResponses[keyof ListRolesApiV1SystemRoleGetResponses]; -export type CreateRoleApiV1RolesPostData = { +export type CreateRoleApiV1SystemRolePostData = { body?: never; path?: never; query: { @@ -1235,10 +1822,10 @@ export type CreateRoleApiV1RolesPostData = { */ is_only_user?: boolean; }; - url: '/api/v1/roles'; + url: '/api/v1/system/role'; }; -export type CreateRoleApiV1RolesPostErrors = { +export type CreateRoleApiV1SystemRolePostErrors = { /** * 角色名称已存在 */ @@ -1249,18 +1836,18 @@ export type CreateRoleApiV1RolesPostErrors = { 422: HttpValidationError; }; -export type CreateRoleApiV1RolesPostError = CreateRoleApiV1RolesPostErrors[keyof CreateRoleApiV1RolesPostErrors]; +export type CreateRoleApiV1SystemRolePostError = CreateRoleApiV1SystemRolePostErrors[keyof CreateRoleApiV1SystemRolePostErrors]; -export type CreateRoleApiV1RolesPostResponses = { +export type CreateRoleApiV1SystemRolePostResponses = { /** * Successful Response */ 200: Role; }; -export type CreateRoleApiV1RolesPostResponse = CreateRoleApiV1RolesPostResponses[keyof CreateRoleApiV1RolesPostResponses]; +export type CreateRoleApiV1SystemRolePostResponse = CreateRoleApiV1SystemRolePostResponses[keyof CreateRoleApiV1SystemRolePostResponses]; -export type DeleteRoleApiV1RolesRoleIdDeleteData = { +export type DeleteRoleApiV1SystemRoleRoleIdDeleteData = { body?: never; path: { /** @@ -1269,10 +1856,10 @@ export type DeleteRoleApiV1RolesRoleIdDeleteData = { role_id: number; }; query?: never; - url: '/api/v1/roles/{role_id}'; + url: '/api/v1/system/role/{role_id}'; }; -export type DeleteRoleApiV1RolesRoleIdDeleteErrors = { +export type DeleteRoleApiV1SystemRoleRoleIdDeleteErrors = { /** * 角色不为空,无法删除 */ @@ -1287,16 +1874,16 @@ export type DeleteRoleApiV1RolesRoleIdDeleteErrors = { 422: HttpValidationError; }; -export type DeleteRoleApiV1RolesRoleIdDeleteError = DeleteRoleApiV1RolesRoleIdDeleteErrors[keyof DeleteRoleApiV1RolesRoleIdDeleteErrors]; +export type DeleteRoleApiV1SystemRoleRoleIdDeleteError = DeleteRoleApiV1SystemRoleRoleIdDeleteErrors[keyof DeleteRoleApiV1SystemRoleRoleIdDeleteErrors]; -export type DeleteRoleApiV1RolesRoleIdDeleteResponses = { +export type DeleteRoleApiV1SystemRoleRoleIdDeleteResponses = { /** * Successful Response */ 200: unknown; }; -export type GetRoleApiV1RolesRoleIdGetData = { +export type GetRoleApiV1SystemRoleRoleIdGetData = { body?: never; path: { /** @@ -1305,10 +1892,10 @@ export type GetRoleApiV1RolesRoleIdGetData = { role_id: number; }; query?: never; - url: '/api/v1/roles/{role_id}'; + url: '/api/v1/system/role/{role_id}'; }; -export type GetRoleApiV1RolesRoleIdGetErrors = { +export type GetRoleApiV1SystemRoleRoleIdGetErrors = { /** * 角色不存在 */ @@ -1319,18 +1906,18 @@ export type GetRoleApiV1RolesRoleIdGetErrors = { 422: HttpValidationError; }; -export type GetRoleApiV1RolesRoleIdGetError = GetRoleApiV1RolesRoleIdGetErrors[keyof GetRoleApiV1RolesRoleIdGetErrors]; +export type GetRoleApiV1SystemRoleRoleIdGetError = GetRoleApiV1SystemRoleRoleIdGetErrors[keyof GetRoleApiV1SystemRoleRoleIdGetErrors]; -export type GetRoleApiV1RolesRoleIdGetResponses = { +export type GetRoleApiV1SystemRoleRoleIdGetResponses = { /** * Successful Response */ 200: Role; }; -export type GetRoleApiV1RolesRoleIdGetResponse = GetRoleApiV1RolesRoleIdGetResponses[keyof GetRoleApiV1RolesRoleIdGetResponses]; +export type GetRoleApiV1SystemRoleRoleIdGetResponse = GetRoleApiV1SystemRoleRoleIdGetResponses[keyof GetRoleApiV1SystemRoleRoleIdGetResponses]; -export type UpdateRoleApiV1RolesRoleIdPutData = { +export type UpdateRoleApiV1SystemRoleRoleIdPutData = { body: RoleUpdate; path: { /** @@ -1339,10 +1926,10 @@ export type UpdateRoleApiV1RolesRoleIdPutData = { role_id: number; }; query?: never; - url: '/api/v1/roles/{role_id}'; + url: '/api/v1/system/role/{role_id}'; }; -export type UpdateRoleApiV1RolesRoleIdPutErrors = { +export type UpdateRoleApiV1SystemRoleRoleIdPutErrors = { /** * 角色不存在 */ @@ -1353,18 +1940,18 @@ export type UpdateRoleApiV1RolesRoleIdPutErrors = { 422: HttpValidationError; }; -export type UpdateRoleApiV1RolesRoleIdPutError = UpdateRoleApiV1RolesRoleIdPutErrors[keyof UpdateRoleApiV1RolesRoleIdPutErrors]; +export type UpdateRoleApiV1SystemRoleRoleIdPutError = UpdateRoleApiV1SystemRoleRoleIdPutErrors[keyof UpdateRoleApiV1SystemRoleRoleIdPutErrors]; -export type UpdateRoleApiV1RolesRoleIdPutResponses = { +export type UpdateRoleApiV1SystemRoleRoleIdPutResponses = { /** * Successful Response */ 200: Role; }; -export type UpdateRoleApiV1RolesRoleIdPutResponse = UpdateRoleApiV1RolesRoleIdPutResponses[keyof UpdateRoleApiV1RolesRoleIdPutResponses]; +export type UpdateRoleApiV1SystemRoleRoleIdPutResponse = UpdateRoleApiV1SystemRoleRoleIdPutResponses[keyof UpdateRoleApiV1SystemRoleRoleIdPutResponses]; -export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData = { +export type AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostData = { body?: never; path: { /** @@ -1377,10 +1964,10 @@ export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData = { user_id: string; }; query?: never; - url: '/api/v1/roles/{role_id}/assign-to-user/{user_id}'; + url: '/api/v1/system/role/{role_id}/assign-to-user/{user_id}'; }; -export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors = { +export type AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostErrors = { /** * 角色或用户不存在 */ @@ -1391,16 +1978,16 @@ export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors = { 422: HttpValidationError; }; -export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostError = AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors[keyof AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors]; +export type AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostError = AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostErrors[keyof AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostErrors]; -export type AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostResponses = { +export type AssignRoleToUserApiV1SystemRoleRoleIdAssignToUserUserIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData = { +export type UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostData = { body?: never; path: { /** @@ -1413,10 +2000,10 @@ export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData = { user_id: string; }; query?: never; - url: '/api/v1/roles/{role_id}/unbind-from-user/{user_id}'; + url: '/api/v1/system/role/{role_id}/unbind-from-user/{user_id}'; }; -export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors = { +export type UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostErrors = { /** * 用户不存在 */ @@ -1427,16 +2014,16 @@ export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors = { 422: HttpValidationError; }; -export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostError = UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors[keyof UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors]; +export type UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostError = UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostErrors[keyof UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostErrors]; -export type UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostResponses = { +export type UnbindRoleFromUserApiV1SystemRoleRoleIdUnbindFromUserUserIdPostResponses = { /** * Successful Response */ 200: unknown; }; -export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetData = { +export type GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetData = { body?: never; path: { /** @@ -1445,99 +2032,1331 @@ export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetData = { role_id: number; }; query?: never; - url: '/api/v1/roles/{role_id}/permissions'; + url: '/api/v1/system/role/{role_id}/permissions'; }; -export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors = { +export type GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetErrors = { /** * Validation Error */ 422: HttpValidationError; }; -export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetError = GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors[keyof GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors]; +export type GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetError = GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetErrors[keyof GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetErrors]; -export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses = { +export type GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponses = { /** - * Response Get Role Permissions Api V1 Roles Role Id Permissions Get + * Response Get Role Permissions Api V1 System Role Role Id Permissions Get * * Successful Response */ 200: Array; }; -export type GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponse = GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses[keyof GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses]; +export type GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponse = GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponses[keyof GetRolePermissionsApiV1SystemRoleRoleIdPermissionsGetResponses]; -export type AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostData = { - body?: never; - path: { - /** - * Role Id - */ - role_id: number; - /** - * Permission Id - */ - permission_id: number; - }; +export type UploadImageApiV1SystemUploadImagePostData = { + body: BodyUploadImageApiV1SystemUploadImagePost; + path?: never; query?: never; - url: '/api/v1/roles/{role_id}/assign-permission/{permission_id}'; + url: '/api/v1/system/upload/image'; }; -export type AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors = { - /** - * 权限已分配给角色 - */ - 400: HttpError; - /** - * 角色或权限不存在 - */ - 404: HttpError; +export type UploadImageApiV1SystemUploadImagePostErrors = { /** * Validation Error */ 422: HttpValidationError; }; -export type AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostError = AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors[keyof AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors]; +export type UploadImageApiV1SystemUploadImagePostError = UploadImageApiV1SystemUploadImagePostErrors[keyof UploadImageApiV1SystemUploadImagePostErrors]; -export type AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostResponses = { +export type UploadImageApiV1SystemUploadImagePostResponses = { + /** + * Successful Response + */ + 200: UploadResponse; +}; + +export type UploadImageApiV1SystemUploadImagePostResponse = UploadImageApiV1SystemUploadImagePostResponses[keyof UploadImageApiV1SystemUploadImagePostResponses]; + +export type ReadProductsApiV1ErpProductsGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/products/'; +}; + +export type ReadProductsApiV1ErpProductsGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadProductsApiV1ErpProductsGetError = ReadProductsApiV1ErpProductsGetErrors[keyof ReadProductsApiV1ErpProductsGetErrors]; + +export type ReadProductsApiV1ErpProductsGetResponses = { + /** + * Response Read Products Api V1 Erp Products Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadProductsApiV1ErpProductsGetResponse = ReadProductsApiV1ErpProductsGetResponses[keyof ReadProductsApiV1ErpProductsGetResponses]; + +export type CreateProductApiV1ErpProductsPostData = { + body: ProductCreate; + path?: never; + query?: never; + url: '/api/v1/erp/products/'; +}; + +export type CreateProductApiV1ErpProductsPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateProductApiV1ErpProductsPostError = CreateProductApiV1ErpProductsPostErrors[keyof CreateProductApiV1ErpProductsPostErrors]; + +export type CreateProductApiV1ErpProductsPostResponses = { + /** + * Successful Response + */ + 200: ProductRead; +}; + +export type CreateProductApiV1ErpProductsPostResponse = CreateProductApiV1ErpProductsPostResponses[keyof CreateProductApiV1ErpProductsPostResponses]; + +export type DeleteProductApiV1ErpProductsProductIdDeleteData = { + body?: never; + path: { + /** + * Product Id + */ + product_id: unknown; + }; + query?: never; + url: '/api/v1/erp/products/{product_id}'; +}; + +export type DeleteProductApiV1ErpProductsProductIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteProductApiV1ErpProductsProductIdDeleteError = DeleteProductApiV1ErpProductsProductIdDeleteErrors[keyof DeleteProductApiV1ErpProductsProductIdDeleteErrors]; + +export type DeleteProductApiV1ErpProductsProductIdDeleteResponses = { /** * Successful Response */ 200: unknown; }; -export type UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostData = { +export type ReadProductApiV1ErpProductsProductIdGetData = { body?: never; path: { /** - * Role Id + * Product Id */ - role_id: number; - /** - * Permission Id - */ - permission_id: number; + product_id: unknown; }; query?: never; - url: '/api/v1/roles/{role_id}/unbind-permission/{permission_id}'; + url: '/api/v1/erp/products/{product_id}'; }; -export type UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors = { - /** - * 角色不存在 - */ - 404: HttpError; +export type ReadProductApiV1ErpProductsProductIdGetErrors = { /** * Validation Error */ 422: HttpValidationError; }; -export type UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostError = UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors[keyof UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors]; +export type ReadProductApiV1ErpProductsProductIdGetError = ReadProductApiV1ErpProductsProductIdGetErrors[keyof ReadProductApiV1ErpProductsProductIdGetErrors]; -export type UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostResponses = { +export type ReadProductApiV1ErpProductsProductIdGetResponses = { + /** + * Successful Response + */ + 200: ProductRead; +}; + +export type ReadProductApiV1ErpProductsProductIdGetResponse = ReadProductApiV1ErpProductsProductIdGetResponses[keyof ReadProductApiV1ErpProductsProductIdGetResponses]; + +export type UpdateProductApiV1ErpProductsProductIdPatchData = { + body: ProductUpdate; + path: { + /** + * Product Id + */ + product_id: unknown; + }; + query?: never; + url: '/api/v1/erp/products/{product_id}'; +}; + +export type UpdateProductApiV1ErpProductsProductIdPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateProductApiV1ErpProductsProductIdPatchError = UpdateProductApiV1ErpProductsProductIdPatchErrors[keyof UpdateProductApiV1ErpProductsProductIdPatchErrors]; + +export type UpdateProductApiV1ErpProductsProductIdPatchResponses = { + /** + * Successful Response + */ + 200: ProductRead; +}; + +export type UpdateProductApiV1ErpProductsProductIdPatchResponse = UpdateProductApiV1ErpProductsProductIdPatchResponses[keyof UpdateProductApiV1ErpProductsProductIdPatchResponses]; + +export type ReadWarehousesApiV1ErpWarehousesGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/warehouses/'; +}; + +export type ReadWarehousesApiV1ErpWarehousesGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadWarehousesApiV1ErpWarehousesGetError = ReadWarehousesApiV1ErpWarehousesGetErrors[keyof ReadWarehousesApiV1ErpWarehousesGetErrors]; + +export type ReadWarehousesApiV1ErpWarehousesGetResponses = { + /** + * Response Read Warehouses Api V1 Erp Warehouses Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadWarehousesApiV1ErpWarehousesGetResponse = ReadWarehousesApiV1ErpWarehousesGetResponses[keyof ReadWarehousesApiV1ErpWarehousesGetResponses]; + +export type CreateWarehouseApiV1ErpWarehousesPostData = { + body: WarehouseCreate; + path?: never; + query?: never; + url: '/api/v1/erp/warehouses/'; +}; + +export type CreateWarehouseApiV1ErpWarehousesPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateWarehouseApiV1ErpWarehousesPostError = CreateWarehouseApiV1ErpWarehousesPostErrors[keyof CreateWarehouseApiV1ErpWarehousesPostErrors]; + +export type CreateWarehouseApiV1ErpWarehousesPostResponses = { + /** + * Successful Response + */ + 200: WarehouseRead; +}; + +export type CreateWarehouseApiV1ErpWarehousesPostResponse = CreateWarehouseApiV1ErpWarehousesPostResponses[keyof CreateWarehouseApiV1ErpWarehousesPostResponses]; + +export type DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteData = { + body?: never; + path: { + /** + * Warehouse Id + */ + warehouse_id: unknown; + }; + query?: never; + url: '/api/v1/erp/warehouses/{warehouse_id}'; +}; + +export type DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteError = DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteErrors[keyof DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteErrors]; + +export type DeleteWarehouseApiV1ErpWarehousesWarehouseIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadWarehouseApiV1ErpWarehousesWarehouseIdGetData = { + body?: never; + path: { + /** + * Warehouse Id + */ + warehouse_id: unknown; + }; + query?: never; + url: '/api/v1/erp/warehouses/{warehouse_id}'; +}; + +export type ReadWarehouseApiV1ErpWarehousesWarehouseIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadWarehouseApiV1ErpWarehousesWarehouseIdGetError = ReadWarehouseApiV1ErpWarehousesWarehouseIdGetErrors[keyof ReadWarehouseApiV1ErpWarehousesWarehouseIdGetErrors]; + +export type ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponses = { + /** + * Successful Response + */ + 200: WarehouseRead; +}; + +export type ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponse = ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponses[keyof ReadWarehouseApiV1ErpWarehousesWarehouseIdGetResponses]; + +export type UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchData = { + body: WarehouseUpdate; + path: { + /** + * Warehouse Id + */ + warehouse_id: unknown; + }; + query?: never; + url: '/api/v1/erp/warehouses/{warehouse_id}'; +}; + +export type UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchError = UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchErrors[keyof UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchErrors]; + +export type UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponses = { + /** + * Successful Response + */ + 200: WarehouseRead; +}; + +export type UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponse = UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponses[keyof UpdateWarehouseApiV1ErpWarehousesWarehouseIdPatchResponses]; + +export type ReadInventoryApiV1ErpInventoryGetData = { + body?: never; + path?: never; + query?: { + /** + * Warehouse Id + */ + warehouse_id?: string; + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/inventory/'; +}; + +export type ReadInventoryApiV1ErpInventoryGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadInventoryApiV1ErpInventoryGetError = ReadInventoryApiV1ErpInventoryGetErrors[keyof ReadInventoryApiV1ErpInventoryGetErrors]; + +export type ReadInventoryApiV1ErpInventoryGetResponses = { + /** + * Response Read Inventory Api V1 Erp Inventory Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadInventoryApiV1ErpInventoryGetResponse = ReadInventoryApiV1ErpInventoryGetResponses[keyof ReadInventoryApiV1ErpInventoryGetResponses]; + +export type ReadInventoryByProductApiV1ErpInventoryProductProductIdGetData = { + body?: never; + path: { + /** + * Product Id + */ + product_id: unknown; + }; + query?: never; + url: '/api/v1/erp/inventory/product/{product_id}'; +}; + +export type ReadInventoryByProductApiV1ErpInventoryProductProductIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadInventoryByProductApiV1ErpInventoryProductProductIdGetError = ReadInventoryByProductApiV1ErpInventoryProductProductIdGetErrors[keyof ReadInventoryByProductApiV1ErpInventoryProductProductIdGetErrors]; + +export type ReadInventoryByProductApiV1ErpInventoryProductProductIdGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type AdjustInventoryApiV1ErpInventoryAdjustPostData = { + body: InventoryAdjustRequest; + path?: never; + query?: never; + url: '/api/v1/erp/inventory/adjust'; +}; + +export type AdjustInventoryApiV1ErpInventoryAdjustPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type AdjustInventoryApiV1ErpInventoryAdjustPostError = AdjustInventoryApiV1ErpInventoryAdjustPostErrors[keyof AdjustInventoryApiV1ErpInventoryAdjustPostErrors]; + +export type AdjustInventoryApiV1ErpInventoryAdjustPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadMovementsApiV1ErpInventoryMovementsGetData = { + body?: never; + path?: never; + query?: { + /** + * Product Id + */ + product_id?: string; + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/inventory/movements'; +}; + +export type ReadMovementsApiV1ErpInventoryMovementsGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadMovementsApiV1ErpInventoryMovementsGetError = ReadMovementsApiV1ErpInventoryMovementsGetErrors[keyof ReadMovementsApiV1ErpInventoryMovementsGetErrors]; + +export type ReadMovementsApiV1ErpInventoryMovementsGetResponses = { + /** + * Response Read Movements Api V1 Erp Inventory Movements Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadMovementsApiV1ErpInventoryMovementsGetResponse = ReadMovementsApiV1ErpInventoryMovementsGetResponses[keyof ReadMovementsApiV1ErpInventoryMovementsGetResponses]; + +export type ReadSuppliersApiV1ErpSuppliersGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/suppliers/'; +}; + +export type ReadSuppliersApiV1ErpSuppliersGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSuppliersApiV1ErpSuppliersGetError = ReadSuppliersApiV1ErpSuppliersGetErrors[keyof ReadSuppliersApiV1ErpSuppliersGetErrors]; + +export type ReadSuppliersApiV1ErpSuppliersGetResponses = { + /** + * Response Read Suppliers Api V1 Erp Suppliers Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadSuppliersApiV1ErpSuppliersGetResponse = ReadSuppliersApiV1ErpSuppliersGetResponses[keyof ReadSuppliersApiV1ErpSuppliersGetResponses]; + +export type CreateSupplierApiV1ErpSuppliersPostData = { + body: SupplierCreate; + path?: never; + query?: never; + url: '/api/v1/erp/suppliers/'; +}; + +export type CreateSupplierApiV1ErpSuppliersPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateSupplierApiV1ErpSuppliersPostError = CreateSupplierApiV1ErpSuppliersPostErrors[keyof CreateSupplierApiV1ErpSuppliersPostErrors]; + +export type CreateSupplierApiV1ErpSuppliersPostResponses = { + /** + * Successful Response + */ + 200: SupplierRead; +}; + +export type CreateSupplierApiV1ErpSuppliersPostResponse = CreateSupplierApiV1ErpSuppliersPostResponses[keyof CreateSupplierApiV1ErpSuppliersPostResponses]; + +export type DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteData = { + body?: never; + path: { + /** + * Supplier Id + */ + supplier_id: unknown; + }; + query?: never; + url: '/api/v1/erp/suppliers/{supplier_id}'; +}; + +export type DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteError = DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteErrors[keyof DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteErrors]; + +export type DeleteSupplierApiV1ErpSuppliersSupplierIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadSupplierApiV1ErpSuppliersSupplierIdGetData = { + body?: never; + path: { + /** + * Supplier Id + */ + supplier_id: unknown; + }; + query?: never; + url: '/api/v1/erp/suppliers/{supplier_id}'; +}; + +export type ReadSupplierApiV1ErpSuppliersSupplierIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSupplierApiV1ErpSuppliersSupplierIdGetError = ReadSupplierApiV1ErpSuppliersSupplierIdGetErrors[keyof ReadSupplierApiV1ErpSuppliersSupplierIdGetErrors]; + +export type ReadSupplierApiV1ErpSuppliersSupplierIdGetResponses = { + /** + * Successful Response + */ + 200: SupplierRead; +}; + +export type ReadSupplierApiV1ErpSuppliersSupplierIdGetResponse = ReadSupplierApiV1ErpSuppliersSupplierIdGetResponses[keyof ReadSupplierApiV1ErpSuppliersSupplierIdGetResponses]; + +export type UpdateSupplierApiV1ErpSuppliersSupplierIdPatchData = { + body: SupplierUpdate; + path: { + /** + * Supplier Id + */ + supplier_id: unknown; + }; + query?: never; + url: '/api/v1/erp/suppliers/{supplier_id}'; +}; + +export type UpdateSupplierApiV1ErpSuppliersSupplierIdPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateSupplierApiV1ErpSuppliersSupplierIdPatchError = UpdateSupplierApiV1ErpSuppliersSupplierIdPatchErrors[keyof UpdateSupplierApiV1ErpSuppliersSupplierIdPatchErrors]; + +export type UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponses = { + /** + * Successful Response + */ + 200: SupplierRead; +}; + +export type UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponse = UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponses[keyof UpdateSupplierApiV1ErpSuppliersSupplierIdPatchResponses]; + +export type ReadCustomersApiV1ErpCustomersGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/customers/'; +}; + +export type ReadCustomersApiV1ErpCustomersGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadCustomersApiV1ErpCustomersGetError = ReadCustomersApiV1ErpCustomersGetErrors[keyof ReadCustomersApiV1ErpCustomersGetErrors]; + +export type ReadCustomersApiV1ErpCustomersGetResponses = { + /** + * Response Read Customers Api V1 Erp Customers Get + * + * Successful Response + */ + 200: Array; +}; + +export type ReadCustomersApiV1ErpCustomersGetResponse = ReadCustomersApiV1ErpCustomersGetResponses[keyof ReadCustomersApiV1ErpCustomersGetResponses]; + +export type CreateCustomerApiV1ErpCustomersPostData = { + body: CustomerCreate; + path?: never; + query?: never; + url: '/api/v1/erp/customers/'; +}; + +export type CreateCustomerApiV1ErpCustomersPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateCustomerApiV1ErpCustomersPostError = CreateCustomerApiV1ErpCustomersPostErrors[keyof CreateCustomerApiV1ErpCustomersPostErrors]; + +export type CreateCustomerApiV1ErpCustomersPostResponses = { + /** + * Successful Response + */ + 200: CustomerRead; +}; + +export type CreateCustomerApiV1ErpCustomersPostResponse = CreateCustomerApiV1ErpCustomersPostResponses[keyof CreateCustomerApiV1ErpCustomersPostResponses]; + +export type DeleteCustomerApiV1ErpCustomersCustomerIdDeleteData = { + body?: never; + path: { + /** + * Customer Id + */ + customer_id: unknown; + }; + query?: never; + url: '/api/v1/erp/customers/{customer_id}'; +}; + +export type DeleteCustomerApiV1ErpCustomersCustomerIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteCustomerApiV1ErpCustomersCustomerIdDeleteError = DeleteCustomerApiV1ErpCustomersCustomerIdDeleteErrors[keyof DeleteCustomerApiV1ErpCustomersCustomerIdDeleteErrors]; + +export type DeleteCustomerApiV1ErpCustomersCustomerIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadCustomerApiV1ErpCustomersCustomerIdGetData = { + body?: never; + path: { + /** + * Customer Id + */ + customer_id: unknown; + }; + query?: never; + url: '/api/v1/erp/customers/{customer_id}'; +}; + +export type ReadCustomerApiV1ErpCustomersCustomerIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadCustomerApiV1ErpCustomersCustomerIdGetError = ReadCustomerApiV1ErpCustomersCustomerIdGetErrors[keyof ReadCustomerApiV1ErpCustomersCustomerIdGetErrors]; + +export type ReadCustomerApiV1ErpCustomersCustomerIdGetResponses = { + /** + * Successful Response + */ + 200: CustomerRead; +}; + +export type ReadCustomerApiV1ErpCustomersCustomerIdGetResponse = ReadCustomerApiV1ErpCustomersCustomerIdGetResponses[keyof ReadCustomerApiV1ErpCustomersCustomerIdGetResponses]; + +export type UpdateCustomerApiV1ErpCustomersCustomerIdPatchData = { + body: CustomerUpdate; + path: { + /** + * Customer Id + */ + customer_id: unknown; + }; + query?: never; + url: '/api/v1/erp/customers/{customer_id}'; +}; + +export type UpdateCustomerApiV1ErpCustomersCustomerIdPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateCustomerApiV1ErpCustomersCustomerIdPatchError = UpdateCustomerApiV1ErpCustomersCustomerIdPatchErrors[keyof UpdateCustomerApiV1ErpCustomersCustomerIdPatchErrors]; + +export type UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponses = { + /** + * Successful Response + */ + 200: CustomerRead; +}; + +export type UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponse = UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponses[keyof UpdateCustomerApiV1ErpCustomersCustomerIdPatchResponses]; + +export type ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/purchase-orders/'; +}; + +export type ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetError = ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetErrors[keyof ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetErrors]; + +export type ReadPurchaseOrdersApiV1ErpPurchaseOrdersGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type CreatePurchaseOrderApiV1ErpPurchaseOrdersPostData = { + body: PurchaseOrderCreate; + path?: never; + query?: never; + url: '/api/v1/erp/purchase-orders/'; +}; + +export type CreatePurchaseOrderApiV1ErpPurchaseOrdersPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreatePurchaseOrderApiV1ErpPurchaseOrdersPostError = CreatePurchaseOrderApiV1ErpPurchaseOrdersPostErrors[keyof CreatePurchaseOrderApiV1ErpPurchaseOrdersPostErrors]; + +export type CreatePurchaseOrderApiV1ErpPurchaseOrdersPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteData = { + body?: never; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-orders/{order_id}'; +}; + +export type DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteError = DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteErrors[keyof DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteErrors]; + +export type DeletePurchaseOrderApiV1ErpPurchaseOrdersOrderIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetData = { + body?: never; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-orders/{order_id}'; +}; + +export type ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetError = ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetErrors[keyof ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetErrors]; + +export type ReadPurchaseOrderApiV1ErpPurchaseOrdersOrderIdGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchData = { + body: PurchaseOrderUpdate; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-orders/{order_id}/status'; +}; + +export type UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchError = UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchErrors[keyof UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchErrors]; + +export type UpdatePurchaseOrderStatusApiV1ErpPurchaseOrdersOrderIdStatusPatchResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/purchase-inbounds/'; +}; + +export type ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetError = ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetErrors[keyof ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetErrors]; + +export type ReadPurchaseInboundsApiV1ErpPurchaseInboundsGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type CreatePurchaseInboundApiV1ErpPurchaseInboundsPostData = { + body: PurchaseInboundCreate; + path?: never; + query?: never; + url: '/api/v1/erp/purchase-inbounds/'; +}; + +export type CreatePurchaseInboundApiV1ErpPurchaseInboundsPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreatePurchaseInboundApiV1ErpPurchaseInboundsPostError = CreatePurchaseInboundApiV1ErpPurchaseInboundsPostErrors[keyof CreatePurchaseInboundApiV1ErpPurchaseInboundsPostErrors]; + +export type CreatePurchaseInboundApiV1ErpPurchaseInboundsPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteData = { + body?: never; + path: { + /** + * Inbound Id + */ + inbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-inbounds/{inbound_id}'; +}; + +export type DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteError = DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteErrors[keyof DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteErrors]; + +export type DeletePurchaseInboundApiV1ErpPurchaseInboundsInboundIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetData = { + body?: never; + path: { + /** + * Inbound Id + */ + inbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-inbounds/{inbound_id}'; +}; + +export type ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetError = ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetErrors[keyof ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetErrors]; + +export type ReadPurchaseInboundApiV1ErpPurchaseInboundsInboundIdGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostData = { + body?: never; + path: { + /** + * Inbound Id + */ + inbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/purchase-inbounds/{inbound_id}/approve'; +}; + +export type ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostError = ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostErrors[keyof ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostErrors]; + +export type ApprovePurchaseInboundApiV1ErpPurchaseInboundsInboundIdApprovePostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadSalesOrdersApiV1ErpSalesOrdersGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/sales-orders/'; +}; + +export type ReadSalesOrdersApiV1ErpSalesOrdersGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSalesOrdersApiV1ErpSalesOrdersGetError = ReadSalesOrdersApiV1ErpSalesOrdersGetErrors[keyof ReadSalesOrdersApiV1ErpSalesOrdersGetErrors]; + +export type ReadSalesOrdersApiV1ErpSalesOrdersGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type CreateSalesOrderApiV1ErpSalesOrdersPostData = { + body: SalesOrderCreate; + path?: never; + query?: never; + url: '/api/v1/erp/sales-orders/'; +}; + +export type CreateSalesOrderApiV1ErpSalesOrdersPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateSalesOrderApiV1ErpSalesOrdersPostError = CreateSalesOrderApiV1ErpSalesOrdersPostErrors[keyof CreateSalesOrderApiV1ErpSalesOrdersPostErrors]; + +export type CreateSalesOrderApiV1ErpSalesOrdersPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteData = { + body?: never; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-orders/{order_id}'; +}; + +export type DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteError = DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteErrors[keyof DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteErrors]; + +export type DeleteSalesOrderApiV1ErpSalesOrdersOrderIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetData = { + body?: never; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-orders/{order_id}'; +}; + +export type ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetError = ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetErrors[keyof ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetErrors]; + +export type ReadSalesOrderApiV1ErpSalesOrdersOrderIdGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchData = { + body: SalesOrderUpdate; + path: { + /** + * Order Id + */ + order_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-orders/{order_id}/status'; +}; + +export type UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchError = UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchErrors[keyof UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchErrors]; + +export type UpdateSalesOrderStatusApiV1ErpSalesOrdersOrderIdStatusPatchResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadSalesOutboundsApiV1ErpSalesOutboundsGetData = { + body?: never; + path?: never; + query?: { + /** + * Offset + */ + offset?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/erp/sales-outbounds/'; +}; + +export type ReadSalesOutboundsApiV1ErpSalesOutboundsGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSalesOutboundsApiV1ErpSalesOutboundsGetError = ReadSalesOutboundsApiV1ErpSalesOutboundsGetErrors[keyof ReadSalesOutboundsApiV1ErpSalesOutboundsGetErrors]; + +export type ReadSalesOutboundsApiV1ErpSalesOutboundsGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type CreateSalesOutboundApiV1ErpSalesOutboundsPostData = { + body: SalesOutboundCreate; + path?: never; + query?: never; + url: '/api/v1/erp/sales-outbounds/'; +}; + +export type CreateSalesOutboundApiV1ErpSalesOutboundsPostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type CreateSalesOutboundApiV1ErpSalesOutboundsPostError = CreateSalesOutboundApiV1ErpSalesOutboundsPostErrors[keyof CreateSalesOutboundApiV1ErpSalesOutboundsPostErrors]; + +export type CreateSalesOutboundApiV1ErpSalesOutboundsPostResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteData = { + body?: never; + path: { + /** + * Outbound Id + */ + outbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-outbounds/{outbound_id}'; +}; + +export type DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteError = DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteErrors[keyof DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteErrors]; + +export type DeleteSalesOutboundApiV1ErpSalesOutboundsOutboundIdDeleteResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetData = { + body?: never; + path: { + /** + * Outbound Id + */ + outbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-outbounds/{outbound_id}'; +}; + +export type ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetError = ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetErrors[keyof ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetErrors]; + +export type ReadSalesOutboundApiV1ErpSalesOutboundsOutboundIdGetResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + +export type ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostData = { + body?: never; + path: { + /** + * Outbound Id + */ + outbound_id: unknown; + }; + query?: never; + url: '/api/v1/erp/sales-outbounds/{outbound_id}/approve'; +}; + +export type ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostError = ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostErrors[keyof ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostErrors]; + +export type ApproveSalesOutboundApiV1ErpSalesOutboundsOutboundIdApprovePostResponses = { /** * Successful Response */ diff --git a/src/chongming-web/src/main.ts b/src/chongming-web/src/main.ts index abe59b7..632834d 100644 --- a/src/chongming-web/src/main.ts +++ b/src/chongming-web/src/main.ts @@ -36,4 +36,67 @@ app.use(ElementPlus, { locale: zhCn }) const authStore = useAuthStore() authStore.initAuth() +// ========== 全局请求拦截器:自动注入 JWT Token ========== +// 解决 ERP 视图直接调用 client.get() 时未携带 Authorization 头导致 401 的问题 +client.interceptors.request.use(async (request, options) => { + const store = useAuthStore() + if (store.accessToken && !request.headers.has('Authorization')) { + request.headers.set('Authorization', `Bearer ${store.accessToken}`) + } + return request +}) + +// ========== Token 自动刷新机制 ========== +/** + * 使用 refresh_token 刷新 access_token + * 防止 token 过期导致前端请求返回 401 + */ +async function refreshAccessToken(): Promise { + const store = useAuthStore() + if (!store.refreshToken) return false + + try { + const baseUrl = (client.getConfig() as any).baseUrl || 'http://127.0.0.1:8000' + const response = await fetch(`${baseUrl}/api/v1/system/auth/refresh`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${store.refreshToken}`, + 'Content-Type': 'application/json' + } + }) + if (response.ok) { + const data = await response.json() + store.setTokens(data.access_token, store.refreshToken) + return true + } else { + // 刷新失败,清除 token 并跳转到登录页 + store.clearTokens() + router.push('/login') + return false + } + } catch (e) { + console.error('[Auth] Token refresh failed:', e) + return false + } +} + +// 每 25 分钟刷新一次 access_token(access_token 有效期已改为 1440 分钟) +setInterval(refreshAccessToken, 25 * 60 * 1000) + +// 页面加载后立即检查并刷新 token(如果接近过期时间) +setTimeout(refreshAccessToken, 1000) + +// 在 client 上添加 error 拦截器,当收到 401 时尝试刷新 token +// 注意:此拦截器无法重试原始请求,但可以确保后续请求使用新 token +client.interceptors.error.use(async (error: any, response: Response | undefined) => { + if (response && response.status === 401) { + console.warn('[Auth] Received 401, attempting to refresh token...') + const refreshed = await refreshAccessToken() + if (refreshed) { + console.log('[Auth] Token refreshed successfully') + } + } + return error +}) + app.mount('#app') diff --git a/src/chongming-web/src/router/index.ts b/src/chongming-web/src/router/index.ts index 63a3c92..e9ea3b1 100644 --- a/src/chongming-web/src/router/index.ts +++ b/src/chongming-web/src/router/index.ts @@ -5,7 +5,7 @@ import { useAuthStore } from '@/stores/auth' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ - { path: '/', redirect: '/todos' }, + { path: '/', redirect: '/erp/dashboard' }, { path: '/login', name: 'login', @@ -24,6 +24,98 @@ const router = createRouter({ component: () => import('@/views/TodoView.vue'), meta: { requiresAuth: true }, }, + { + path: '/erp', + component: () => import('@/views/erp/ErpLayout.vue'), + meta: { requiresAuth: true }, + redirect: '/erp/dashboard', + children: [ + { + path: 'dashboard', + name: 'erp-dashboard', + component: () => import('@/views/erp/DashboardView.vue'), + meta: { title: '工作台' }, + }, + { + path: 'products', + name: 'erp-products', + component: () => import('@/views/erp/ProductView.vue'), + meta: { title: '物料管理' }, + }, + { + path: 'warehouses', + name: 'erp-warehouses', + component: () => import('@/views/erp/WarehouseView.vue'), + meta: { title: '仓库管理' }, + }, + { + path: 'suppliers', + name: 'erp-suppliers', + component: () => import('@/views/erp/SupplierView.vue'), + meta: { title: '供应商管理' }, + }, + { + path: 'customers', + name: 'erp-customers', + component: () => import('@/views/erp/CustomerView.vue'), + meta: { title: '客户管理' }, + }, + { + path: 'purchase-orders', + name: 'erp-purchase-orders', + component: () => import('@/views/erp/PurchaseOrderView.vue'), + meta: { title: '采购订单' }, + }, + { + path: 'purchase-inbounds', + name: 'erp-purchase-inbounds', + component: () => import('@/views/erp/PurchaseInboundView.vue'), + meta: { title: '采购入库' }, + }, + { + path: 'sales-orders', + name: 'erp-sales-orders', + component: () => import('@/views/erp/SalesOrderView.vue'), + meta: { title: '销售订单' }, + }, + { + path: 'sales-outbounds', + name: 'erp-sales-outbounds', + component: () => import('@/views/erp/SalesOutboundView.vue'), + meta: { title: '销售出库' }, + }, + { + path: 'inventory', + name: 'erp-inventory', + component: () => import('@/views/erp/InventoryView.vue'), + meta: { title: '库存查询' }, + }, + { + path: 'code-rules', + name: 'erp-code-rules', + component: () => import('@/views/erp/CodeRuleConfigView.vue'), + meta: { title: '编码规则配置' }, + }, + { + path: 'users', + name: 'system-users', + component: () => import('@/views/system/UserView.vue'), + meta: { title: '用户管理' }, + }, + { + path: 'roles', + name: 'system-roles', + component: () => import('@/views/system/RoleView.vue'), + meta: { title: '角色管理' }, + }, + { + path: 'permissions', + name: 'system-permissions', + component: () => import('@/views/system/PermissionView.vue'), + meta: { title: '权限管理' }, + }, + ], + }, ], }) @@ -35,10 +127,10 @@ router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !isAuthenticated) { next('/login') } else if (to.meta.requiresGuest && isAuthenticated) { - next('/todos') + next('/erp/dashboard') } else { next() } }) -export default router \ No newline at end of file +export default router diff --git a/src/chongming-web/src/stores/auth.ts b/src/chongming-web/src/stores/auth.ts index 9ac5acf..231f8b6 100644 --- a/src/chongming-web/src/stores/auth.ts +++ b/src/chongming-web/src/stores/auth.ts @@ -1,19 +1,25 @@ import { defineStore } from "pinia" import { ElMessage } from 'element-plus' import { client } from "@/api/generated/client.gen" -import { loginApiV1AuthLoginPost, registerApiV1AuthRegisterPost } from "@/api/generated" +import { loginApiV1SystemAuthLoginPost, registerApiV1SystemAuthRegisterPost } from "@/api/generated" interface AuthState { accessToken: string | null refreshToken: string | null + userInfo: Record } export const useAuthStore = defineStore("auth", { state: (): AuthState => ({ accessToken: localStorage.getItem("access_token"), refreshToken: localStorage.getItem("refresh_token"), + userInfo: JSON.parse(localStorage.getItem("user_info") || "{}"), }), + getters: { + isLoggedIn: (state) => !!state.accessToken, + userName: (state) => state.userInfo?.email || state.userInfo?.username || '用户', + }, actions: { setTokens(access: string, refresh: string) { this.accessToken = access @@ -23,17 +29,28 @@ export const useAuthStore = defineStore("auth", { client.setConfig({ auth: () => this.accessToken || '' }) + // 解析 JWT 获取用户信息 + try { + const parts = access.split('.') + const payload = JSON.parse(atob(parts[1] || '')) + this.userInfo = payload.data || payload + localStorage.setItem("user_info", JSON.stringify(this.userInfo)) + } catch { + // ignore + } }, clearTokens() { this.accessToken = null this.refreshToken = null + this.userInfo = {} localStorage.removeItem("access_token") localStorage.removeItem("refresh_token") + localStorage.removeItem("user_info") client.setConfig({ auth: undefined }) }, async login(email: string, password: string) { try { - const { data } = await loginApiV1AuthLoginPost({ + const { data } = await loginApiV1SystemAuthLoginPost({ body: { email, password }, throwOnError: true, }) @@ -44,15 +61,14 @@ export const useAuthStore = defineStore("auth", { } return false } catch (error: any) { - // console.error("登录失败", error) ElMessage.error(error.message || "登录失败") return false } }, - async register(email: string, password: string, username?: string) { + async register(email: string, password: string, username?: string, full_name?: string) { try { - await registerApiV1AuthRegisterPost({ - body: { email, password, username }, + await registerApiV1SystemAuthRegisterPost({ + body: { email, password, username, full_name: full_name || username }, throwOnError: true, }) ElMessage.success("注册成功") @@ -74,4 +90,4 @@ export const useAuthStore = defineStore("auth", { } } } -}) \ No newline at end of file +}) diff --git a/src/chongming-web/src/stores/todo.ts b/src/chongming-web/src/stores/todo.ts index 96b71dc..8fab289 100644 --- a/src/chongming-web/src/stores/todo.ts +++ b/src/chongming-web/src/stores/todo.ts @@ -1,17 +1,18 @@ // src/stores/todo.ts import { defineStore } from 'pinia' -import { - readTodosApiV1TodosGet, - createTodoApiV1TodosPost, - updateTodoApiV1TodosTodoIdPatch, - deleteTodoApiV1TodosTodoIdDelete, - type TodoRead, - type TodoCreate, - type TodoUpdate, -} from '@/api/generated' +import { client } from '@/api/generated/client.gen' + +interface TodoItem { + id: number + title: string + description?: string | null + completed: boolean + created_at: string + updated_at: string +} interface TodoState { - todos: TodoRead[] + todos: TodoItem[] loading: boolean } @@ -24,11 +25,11 @@ export const useTodoStore = defineStore('todo', { async fetchTodos() { this.loading = true try { - const { data } = await readTodosApiV1TodosGet({ + const { data } = await client.get({ + url: '/api/v1/todos', query: { offset: 0, limit: 100 }, - throwOnError: true, }) - this.todos = data || [] + this.todos = (data as TodoItem[]) || [] } catch (error) { console.error('获取 Todo 列表失败', error) throw error @@ -36,34 +37,34 @@ export const useTodoStore = defineStore('todo', { this.loading = false } }, - async addTodo(todo: TodoCreate) { - const { data } = await createTodoApiV1TodosPost({ + async addTodo(todo: Partial) { + const { data } = await client.post({ + url: '/api/v1/todos', body: todo, - throwOnError: true, }) if (data) { - this.todos.unshift(data) + this.todos.unshift(data as TodoItem) } return data }, - async updateTodo(id: number, updates: TodoUpdate) { - const { data } = await updateTodoApiV1TodosTodoIdPatch({ + async updateTodo(id: number, updates: Partial) { + const { data } = await client.patch({ + url: '/api/v1/todos/{todo_id}', path: { todo_id: id }, body: updates, - throwOnError: true, }) if (data) { - const index = this.todos.findIndex((t) => t.id === id) - if (index !== -1) this.todos[index] = data + const index = this.todos.findIndex((t: TodoItem) => t.id === id) + if (index !== -1) this.todos[index] = data as TodoItem } - return data + return data as TodoItem }, async deleteTodo(id: number) { - await deleteTodoApiV1TodosTodoIdDelete({ + await client.delete({ + url: '/api/v1/todos/{todo_id}', path: { todo_id: id }, - throwOnError: true, }) - this.todos = this.todos.filter((t) => t.id !== id) + this.todos = this.todos.filter((t: TodoItem) => t.id !== id) }, }, -}) \ No newline at end of file +}) diff --git a/src/chongming-web/src/views/LoginView.vue b/src/chongming-web/src/views/LoginView.vue index ce44549..48819c0 100644 --- a/src/chongming-web/src/views/LoginView.vue +++ b/src/chongming-web/src/views/LoginView.vue @@ -1,58 +1,541 @@ \ No newline at end of file +.particle { + position: absolute; + left: var(--x); + top: var(--y); + width: var(--s); + height: var(--s); + background: radial-gradient(circle, var(--color) 0%, transparent 70%); + opacity: 0.08; + border-radius: 50%; + animation: particle-float var(--d) ease-in-out infinite; + animation-delay: var(--delay); +} +@keyframes particle-float { + 0%, 100% { transform: translate(0, 0) scale(1); } + 25% { transform: translate(30px, -40px) scale(1.1); } + 50% { transform: translate(-20px, 20px) scale(0.9); } + 75% { transform: translate(40px, 30px) scale(1.05); } +} + +/* ========== 主容器 ========== */ +.login-wrapper { + display: flex; + width: 960px; + max-width: 96vw; + min-height: 600px; + background: rgba(255, 255, 255, 0.04); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-radius: 28px; + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 32px 80px rgba(0, 0, 0, 0.4); + overflow: hidden; + z-index: 1; + opacity: 0; + transform: translateY(24px) scale(0.97); + transition: all 0.6s cubic-bezier(0.22, 1, 0.36, 1); +} +.login-wrapper.loaded { + opacity: 1; + transform: translateY(0) scale(1); +} + +/* ========== 左侧品牌面板 ========== */ +.brand-panel { + width: 42%; + background: linear-gradient(160deg, rgba(99, 102, 241, 0.15), rgba(139, 92, 246, 0.08)); + padding: 52px 40px; + display: flex; + flex-direction: column; + justify-content: space-between; + position: relative; + overflow: hidden; +} +.brand-panel::before { + content: ''; + position: absolute; + top: -100px; + right: -100px; + width: 300px; + height: 300px; + background: radial-gradient(circle, rgba(99, 102, 241, 0.15), transparent); + border-radius: 50%; +} + +.brand-content { + position: relative; + z-index: 1; +} +.brand-badge { + display: inline-block; + padding: 4px 14px; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 20px; + font-size: 11px; + color: rgba(255, 255, 255, 0.6); + letter-spacing: 1.5px; + text-transform: uppercase; + margin-bottom: 24px; +} +.brand-icon { + margin-bottom: 20px; +} +.brand-title { + font-size: 32px; + font-weight: 800; + color: #fff; + margin: 0 0 6px 0; + letter-spacing: -0.5px; +} +.brand-subtitle { + font-size: 15px; + color: rgba(255, 255, 255, 0.55); + margin: 0 0 20px 0; + font-weight: 400; +} +.brand-desc p { + font-size: 13px; + color: rgba(255, 255, 255, 0.45); + line-height: 1.6; + margin: 0; +} + +.brand-features { + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 28px; +} +.feature-item { + display: flex; + align-items: center; + gap: 12px; + font-size: 13.5px; + color: rgba(255, 255, 255, 0.75); +} +.feature-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.brand-footer { + position: relative; + z-index: 1; +} +.brand-version { + font-size: 12px; + color: rgba(255, 255, 255, 0.25); + font-family: 'SF Mono', monospace; +} + +/* ========== 右侧表单面板 ========== */ +.form-panel { + width: 58%; + display: flex; + align-items: center; + justify-content: center; + padding: 52px 48px; +} +.form-container { + width: 100%; + max-width: 380px; +} +.form-header { + margin-bottom: 36px; +} +.form-title { + font-size: 28px; + font-weight: 700; + color: #fff; + margin: 0 0 8px 0; + letter-spacing: -0.3px; +} +.form-subtitle { + font-size: 14px; + color: rgba(255, 255, 255, 0.4); + margin: 0; +} + +/* ========== 表单 ========== */ +.login-form { + display: flex; + flex-direction: column; + gap: 22px; +} + +.input-field { + display: flex; + flex-direction: column; + gap: 8px; +} +.input-label { + font-size: 13px; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); + letter-spacing: 0.2px; +} +.input-box { + display: flex; + align-items: center; + gap: 10px; + background: rgba(255, 255, 255, 0.06); + border: 1.5px solid rgba(255, 255, 255, 0.08); + border-radius: 14px; + padding: 0 16px; + transition: all 0.3s ease; + min-height: 48px; +} +.input-box:focus-within, +.input-box.focused { + border-color: #6366f1; + background: rgba(255, 255, 255, 0.08); + box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); +} +.input-icon { + color: rgba(255, 255, 255, 0.3); + flex-shrink: 0; + transition: color 0.3s; +} +.input-box:focus-within .input-icon { + color: #6366f1; +} +.input-control { + flex: 1; + background: none; + border: none; + outline: none; + color: #fff; + font-size: 14px; + padding: 12px 0; + width: 100%; + font-family: inherit; +} +.input-control::placeholder { + color: rgba(255, 255, 255, 0.2); +} +.input-control:-webkit-autofill, +.input-control:-webkit-autofill:hover, +.input-control:-webkit-autofill:focus { + -webkit-text-fill-color: #fff; + -webkit-box-shadow: 0 0 0px 1000px rgba(15, 13, 46, 0.8) inset !important; + transition: background-color 5000s ease-in-out 0s; +} +.toggle-btn { + background: none; + border: none; + cursor: pointer; + color: rgba(255, 255, 255, 0.3); + padding: 4px; + flex-shrink: 0; + display: flex; + align-items: center; + transition: color 0.2s; +} +.toggle-btn:hover { + color: rgba(255, 255, 255, 0.6); +} + +/* ========== 提交按钮 ========== */ +.submit-btn { + margin-top: 8px; + padding: 0; + height: 50px; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + border: none; + border-radius: 14px; + color: #fff; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; +} +.submit-btn::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, transparent, rgba(255,255,255,0.1)); + opacity: 0; + transition: opacity 0.3s; +} +.submit-btn:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 8px 30px rgba(99, 102, 241, 0.35); +} +.submit-btn:hover:not(:disabled)::before { + opacity: 1; +} +.submit-btn:active:not(:disabled) { + transform: translateY(0); + box-shadow: 0 4px 15px rgba(99, 102, 241, 0.25); +} +.submit-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} +.btn-text { + position: relative; + z-index: 1; + letter-spacing: 4px; +} +.spinner { + width: 22px; + height: 22px; + border: 2.5px solid rgba(255, 255, 255, 0.2); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.7s linear infinite; +} +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* ========== 底部链接 ========== */ +.form-footer { + text-align: center; + margin-top: 32px; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; +} +.footer-text { + font-size: 14px; + color: rgba(255, 255, 255, 0.35); +} +.link-btn { + background: none; + border: none; + color: #818cf8; + cursor: pointer; + font-size: 14px; + font-weight: 600; + padding: 0 2px; + transition: all 0.2s; + font-family: inherit; +} +.link-btn:hover { + color: #a5b4fc; +} + +/* ========== 响应式 ========== */ +@media (max-width: 820px) { + .brand-panel { + display: none; + } + .form-panel { + width: 100%; + padding: 48px 32px; + } + .login-wrapper { + width: 100%; + max-width: 440px; + min-height: auto; + } +} + +@media (max-width: 480px) { + .form-panel { + padding: 36px 24px; + } + .form-title { + font-size: 24px; + } + .login-wrapper { + border-radius: 20px; + } +} + diff --git a/src/chongming-web/src/views/RegisterView.vue b/src/chongming-web/src/views/RegisterView.vue index 9851f3f..226e6f3 100644 --- a/src/chongming-web/src/views/RegisterView.vue +++ b/src/chongming-web/src/views/RegisterView.vue @@ -1,61 +1,370 @@ \ No newline at end of file +.particle { + position: absolute; + left: var(--x); top: var(--y); + width: var(--s); height: var(--s); + background: radial-gradient(circle, var(--color) 0%, transparent 70%); + opacity: 0.07; + border-radius: 50%; + animation: particle-float var(--d) ease-in-out infinite; + animation-delay: var(--delay); +} +@keyframes particle-float { + 0%, 100% { transform: translate(0, 0) scale(1); } + 25% { transform: translate(30px, -40px) scale(1.1); } + 50% { transform: translate(-20px, 20px) scale(0.9); } + 75% { transform: translate(40px, 30px) scale(1.05); } +} + +.register-wrapper { + width: 480px; + max-width: 95vw; + background: rgba(255, 255, 255, 0.04); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-radius: 28px; + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: 0 32px 80px rgba(0, 0, 0, 0.4); + padding: 44px 40px; + z-index: 1; + opacity: 0; + transform: translateY(24px) scale(0.97); + transition: all 0.6s cubic-bezier(0.22, 1, 0.36, 1); +} +.register-wrapper.loaded { + opacity: 1; + transform: translateY(0) scale(1); +} + +.brand-mini { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 32px; +} +.brand-name { + font-size: 16px; + font-weight: 700; + color: rgba(255, 255, 255, 0.6); + letter-spacing: 0.5px; +} + +.form-header { + margin-bottom: 32px; +} +.form-title { + font-size: 26px; + font-weight: 700; + color: #fff; + margin: 0 0 6px 0; + letter-spacing: -0.3px; +} +.form-subtitle { + font-size: 14px; + color: rgba(255, 255, 255, 0.4); + margin: 0; +} + +.register-form { + display: flex; + flex-direction: column; + gap: 18px; +} + +.input-row { + display: flex; + gap: 14px; +} +.input-row .input-field { + flex: 1; +} + +.input-field { + display: flex; + flex-direction: column; + gap: 7px; +} +.input-label { + font-size: 13px; + font-weight: 500; + color: rgba(255, 255, 255, 0.6); +} +.required { + color: #f87171; +} +.input-box { + display: flex; + align-items: center; + gap: 10px; + background: rgba(255, 255, 255, 0.06); + border: 1.5px solid rgba(255, 255, 255, 0.08); + border-radius: 12px; + padding: 0 14px; + transition: all 0.3s ease; + min-height: 46px; +} +.input-box:focus-within, +.input-box.focused { + border-color: #6366f1; + background: rgba(255, 255, 255, 0.08); + box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); +} +.input-icon { color: rgba(255, 255, 255, 0.3); flex-shrink: 0; transition: color 0.3s; } +.input-box:focus-within .input-icon { color: #6366f1; } +.input-control { + flex: 1; + background: none; + border: none; + outline: none; + color: #fff; + font-size: 14px; + padding: 11px 0; + width: 100%; + font-family: inherit; +} +.input-control::placeholder { color: rgba(255, 255, 255, 0.2); } +.input-control:-webkit-autofill, +.input-control:-webkit-autofill:hover, +.input-control:-webkit-autofill:focus { + -webkit-text-fill-color: #fff; + -webkit-box-shadow: 0 0 0px 1000px rgba(15, 13, 46, 0.8) inset !important; +} +.toggle-btn { + background: none; border: none; cursor: pointer; + color: rgba(255, 255, 255, 0.3); padding: 4px; + flex-shrink: 0; display: flex; align-items: center; + transition: color 0.2s; +} +.toggle-btn:hover { color: rgba(255, 255, 255, 0.6); } + +.submit-btn { + margin-top: 6px; + padding: 0; + height: 48px; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + border: none; + border-radius: 14px; + color: #fff; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; +} +.submit-btn::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, transparent, rgba(255,255,255,0.1)); + opacity: 0; + transition: opacity 0.3s; +} +.submit-btn:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 8px 30px rgba(99, 102, 241, 0.35); +} +.submit-btn:hover:not(:disabled)::before { opacity: 1; } +.submit-btn:active:not(:disabled) { + transform: translateY(0); + box-shadow: 0 4px 15px rgba(99, 102, 241, 0.25); +} +.submit-btn:disabled { opacity: 0.6; cursor: not-allowed; } +.btn-text { position: relative; z-index: 1; letter-spacing: 4px; } +.spinner { + width: 20px; height: 20px; + border: 2.5px solid rgba(255, 255, 255, 0.2); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.7s linear infinite; +} +@keyframes spin { to { transform: rotate(360deg); } } + +.form-footer { + text-align: center; + margin-top: 28px; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; +} +.footer-text { font-size: 14px; color: rgba(255, 255, 255, 0.35); } +.link-btn { + background: none; border: none; + color: #818cf8; cursor: pointer; + font-size: 14px; font-weight: 600; + padding: 0 2px; transition: all 0.2s; + font-family: inherit; +} +.link-btn:hover { color: #a5b4fc; } + +@media (max-width: 480px) { + .register-wrapper { padding: 32px 24px; } + .input-row { flex-direction: column; gap: 18px; } + .form-title { font-size: 22px; } +} + diff --git a/src/chongming-web/src/views/TodoView.vue b/src/chongming-web/src/views/TodoView.vue index 7434a99..de5044c 100644 --- a/src/chongming-web/src/views/TodoView.vue +++ b/src/chongming-web/src/views/TodoView.vue @@ -1,245 +1,405 @@ \ No newline at end of file +.delete-btn { + background: none; + border: none; + color: #c0c4cc; + cursor: pointer; + padding: 4px; + border-radius: 6px; + display: flex; + transition: all 0.2s; +} +.delete-btn:hover { + color: #ef4444; + background: rgba(239,68,68,0.08); +} + +@media (max-width: 480px) { + .todo-main { padding: 16px 12px; } + .todo-container { padding: 20px 16px; } + .header-content { padding: 12px 16px; } + .add-todo { flex-direction: column; } +} + diff --git a/src/chongming-web/src/views/erp/CodeRuleConfigView.vue b/src/chongming-web/src/views/erp/CodeRuleConfigView.vue new file mode 100644 index 0000000..77b1107 --- /dev/null +++ b/src/chongming-web/src/views/erp/CodeRuleConfigView.vue @@ -0,0 +1,565 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/CustomerView.vue b/src/chongming-web/src/views/erp/CustomerView.vue new file mode 100644 index 0000000..2796b23 --- /dev/null +++ b/src/chongming-web/src/views/erp/CustomerView.vue @@ -0,0 +1,155 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/DashboardView.vue b/src/chongming-web/src/views/erp/DashboardView.vue new file mode 100644 index 0000000..8856170 --- /dev/null +++ b/src/chongming-web/src/views/erp/DashboardView.vue @@ -0,0 +1,521 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/ErpLayout.vue b/src/chongming-web/src/views/erp/ErpLayout.vue new file mode 100644 index 0000000..1118cff --- /dev/null +++ b/src/chongming-web/src/views/erp/ErpLayout.vue @@ -0,0 +1,595 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/InventoryView.vue b/src/chongming-web/src/views/erp/InventoryView.vue new file mode 100644 index 0000000..3b1d00e --- /dev/null +++ b/src/chongming-web/src/views/erp/InventoryView.vue @@ -0,0 +1,129 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/ProductView.vue b/src/chongming-web/src/views/erp/ProductView.vue new file mode 100644 index 0000000..6df8319 --- /dev/null +++ b/src/chongming-web/src/views/erp/ProductView.vue @@ -0,0 +1,608 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/PurchaseInboundView.vue b/src/chongming-web/src/views/erp/PurchaseInboundView.vue new file mode 100644 index 0000000..e5c3b63 --- /dev/null +++ b/src/chongming-web/src/views/erp/PurchaseInboundView.vue @@ -0,0 +1,527 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/PurchaseOrderView.vue b/src/chongming-web/src/views/erp/PurchaseOrderView.vue new file mode 100644 index 0000000..bc175a9 --- /dev/null +++ b/src/chongming-web/src/views/erp/PurchaseOrderView.vue @@ -0,0 +1,817 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/SalesOrderView.vue b/src/chongming-web/src/views/erp/SalesOrderView.vue new file mode 100644 index 0000000..08c2d60 --- /dev/null +++ b/src/chongming-web/src/views/erp/SalesOrderView.vue @@ -0,0 +1,166 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/SalesOutboundView.vue b/src/chongming-web/src/views/erp/SalesOutboundView.vue new file mode 100644 index 0000000..1e42df4 --- /dev/null +++ b/src/chongming-web/src/views/erp/SalesOutboundView.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/SupplierView.vue b/src/chongming-web/src/views/erp/SupplierView.vue new file mode 100644 index 0000000..b256d05 --- /dev/null +++ b/src/chongming-web/src/views/erp/SupplierView.vue @@ -0,0 +1,157 @@ + + + + + + diff --git a/src/chongming-web/src/views/erp/WarehouseView.vue b/src/chongming-web/src/views/erp/WarehouseView.vue new file mode 100644 index 0000000..272a1be --- /dev/null +++ b/src/chongming-web/src/views/erp/WarehouseView.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/src/chongming-web/src/views/system/PermissionView.vue b/src/chongming-web/src/views/system/PermissionView.vue new file mode 100644 index 0000000..b2dab64 --- /dev/null +++ b/src/chongming-web/src/views/system/PermissionView.vue @@ -0,0 +1,520 @@ + + + + + + diff --git a/src/chongming-web/src/views/system/RoleView.vue b/src/chongming-web/src/views/system/RoleView.vue new file mode 100644 index 0000000..f7cb9d8 --- /dev/null +++ b/src/chongming-web/src/views/system/RoleView.vue @@ -0,0 +1,652 @@ + + + + + + diff --git a/src/chongming-web/src/views/system/UserView.vue b/src/chongming-web/src/views/system/UserView.vue new file mode 100644 index 0000000..320f632 --- /dev/null +++ b/src/chongming-web/src/views/system/UserView.vue @@ -0,0 +1,678 @@ + + + + + + diff --git a/src/chongming-web/vite.config.ts b/src/chongming-web/vite.config.ts index 24e59ab..e7e44e8 100644 --- a/src/chongming-web/vite.config.ts +++ b/src/chongming-web/vite.config.ts @@ -19,4 +19,14 @@ export default defineConfig({ '@': fileURLToPath(new URL('./src', import.meta.url)) }, }, + server: { + host: '0.0.0.0', + port: 5173, + proxy: { + '/api': { + target: 'http://127.0.0.1:8000', + changeOrigin: true, + }, + }, + }, }) diff --git a/src/chongming/app/api/__init__.py b/src/chongming/app/api/__init__.py index 4ea1bf1..039b82e 100644 --- a/src/chongming/app/api/__init__.py +++ b/src/chongming/app/api/__init__.py @@ -4,18 +4,17 @@ API 路由 from fastapi import APIRouter -from .routers import todo, auth, upload, permission, role - +from .routers.erp import erp_router +from .routers.system import system_router # 创建主路由 api_router = APIRouter() -# 注册子路由 -api_router.include_router(todo.router, prefix="/todos", tags=["todo"]) -api_router.include_router(auth.router, prefix="/auth", tags=["authentication"]) -api_router.include_router(upload.router, prefix="/upload", tags=["upload"]) -api_router.include_router(permission.router, prefix="/permissions", tags=["permission"]) -api_router.include_router(role.router, prefix="/roles", tags=["role"]) +# 注册system路由 +api_router.include_router(system_router, prefix="/system") + +# 注册ERP路由 +api_router.include_router(erp_router, prefix="/erp") # 导出路由 diff --git a/src/chongming/app/api/deps.py b/src/chongming/app/api/deps.py index 642e82d..955e27a 100644 --- a/src/chongming/app/api/deps.py +++ b/src/chongming/app/api/deps.py @@ -4,14 +4,13 @@ from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.ext.asyncio import AsyncSession -from ..service.auth import get_auth_service -from ..service.permission import PermissionService -from ..service.user import UserService +from ..service.system.auth import get_auth_service +from ..service.system.permission import PermissionService +from ..service.system.user import UserService from ..core.database import get_session from plugins.jwt.jwt_cache import TokenData - access_scheme = HTTPBearer(scheme_name="AccessToken") @@ -108,7 +107,9 @@ class PermissionChecker: - False: 用户只需拥有任一指定权限 require_superuser: 是否要求超级用户身份(超级用户自动拥有所有权限) """ - self.permissions = [permissions] if isinstance(permissions, str) else permissions + self.permissions = ( + [permissions] if isinstance(permissions, str) else permissions + ) self.require_all = require_all self.require_superuser = require_superuser @@ -156,7 +157,7 @@ class PermissionChecker: ) # 构建用户拥有的权限名称集合 - user_perm_names: Set[str] = {perm.name for perm in user_permissions} + user_perm_names: Set[str] = set(user_permissions) # 4. 权限匹配检查 if self.require_all: diff --git a/src/chongming/app/api/routers/erp/__init__.py b/src/chongming/app/api/routers/erp/__init__.py new file mode 100644 index 0000000..859edbd --- /dev/null +++ b/src/chongming/app/api/routers/erp/__init__.py @@ -0,0 +1,31 @@ +from fastapi import APIRouter + +from .product_router import router as product_router +from .warehouse_router import router as warehouse_router +from .inventory_router import router as inventory_router +from .supplier_router import router as supplier_router +from .customer_router import router as customer_router +from .purchase_router import router as purchase_router +from .purchase_inbound_router import router as purchase_inbound_router +from .sales_router import router as sales_router +from .sales_outbound_router import router as sales_outbound_router + +erp_router = APIRouter() + +erp_router.include_router(product_router, prefix="/products", tags=["ERP-产品管理"]) +erp_router.include_router(warehouse_router, prefix="/warehouses", tags=["ERP-仓库管理"]) +erp_router.include_router(inventory_router, prefix="/inventory", tags=["ERP-库存管理"]) +erp_router.include_router(supplier_router, prefix="/suppliers", tags=["ERP-供应商管理"]) +erp_router.include_router(customer_router, prefix="/customers", tags=["ERP-客户管理"]) +erp_router.include_router( + purchase_router, prefix="/purchase-orders", tags=["ERP-采购管理"] +) +erp_router.include_router( + purchase_inbound_router, prefix="/purchase-inbounds", tags=["ERP-采购入库管理"] +) +erp_router.include_router(sales_router, prefix="/sales-orders", tags=["ERP-销售管理"]) +erp_router.include_router( + sales_outbound_router, prefix="/sales-outbounds", tags=["ERP-销售出库管理"] +) + +__all__ = ["erp_router"] diff --git a/src/chongming/app/api/routers/erp/customer_router.py b/src/chongming/app/api/routers/erp/customer_router.py new file mode 100644 index 0000000..d74302e --- /dev/null +++ b/src/chongming/app/api/routers/erp/customer_router.py @@ -0,0 +1,70 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.customer import CustomerCreate, CustomerRead, CustomerUpdate +from ....core.database import get_session +from ....constant.permission.erp.customer import CustomerConstant +from ....service.erp.customer_service import CustomerService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/", response_model=CustomerRead) +async def create_customer( + data: CustomerCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(CustomerConstant.CUSTOMER_CREATE)), +): + return await CustomerService.create(data, session) + + +@router.get("/", response_model=List[CustomerRead]) +async def read_customers( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(CustomerConstant.CUSTOMER_READ)), +): + return await CustomerService.get_all(offset, limit, session) + + +@router.get("/{customer_id}", response_model=CustomerRead) +async def read_customer( + customer_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(CustomerConstant.CUSTOMER_READ)), +): + obj = await CustomerService.get_by_id(customer_id, session) + if not obj: + raise HTTPException(status_code=404, detail="客户不存在") + return obj + + +@router.patch("/{customer_id}", response_model=CustomerRead) +async def update_customer( + customer_id: UUID, + update: CustomerUpdate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(CustomerConstant.CUSTOMER_UPDATE)), +): + obj = await CustomerService.update(customer_id, update, session) + if not obj: + raise HTTPException(status_code=404, detail="客户不存在") + return obj + + +@router.delete("/{customer_id}") +async def delete_customer( + customer_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(CustomerConstant.CUSTOMER_DELETE)), +): + deleted = await CustomerService.delete(customer_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="客户不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/inventory_router.py b/src/chongming/app/api/routers/erp/inventory_router.py new file mode 100644 index 0000000..86fa2e9 --- /dev/null +++ b/src/chongming/app/api/routers/erp/inventory_router.py @@ -0,0 +1,108 @@ +from uuid import UUID +from typing import List, Optional + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from pydantic import BaseModel +from typing import Optional + +from ....model.erp.inventory import ( + Inventory, + InventoryRead, + InventoryMovement, + InventoryMovementRead, +) +from ....core.database import get_session +from ....constant.permission.erp.inventory import InventoryConstant +from ....service.erp.inventory_service import InventoryService +from ...deps import get_current_user, PermissionChecker + + +class InventoryAdjustRequest(BaseModel): + product_id: str + warehouse_id: str + quantity: float + remark: Optional[str] = None + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +def inventory_to_read(inv: Inventory) -> InventoryRead: + return InventoryRead( + id=inv.id, + product_id=inv.product_id, + warehouse_id=inv.warehouse_id, + quantity=inv.quantity, + locked_quantity=inv.locked_quantity, + available_quantity=inv.quantity - inv.locked_quantity, + created_at=inv.created_at, + updated_at=inv.updated_at, + ) + + +@router.get("/", response_model=List[InventoryRead]) +async def read_inventory( + warehouse_id: Optional[str] = None, + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(InventoryConstant.INVENTORY_READ)), +): + if warehouse_id: + records = await InventoryService.get_inventory_by_warehouse( + warehouse_id, offset, limit, session + ) + else: + records = await InventoryService.get_all_inventory(offset, limit, session) + return [inventory_to_read(r) for r in records] + + +@router.get("/product/{product_id}") +async def read_inventory_by_product( + product_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(InventoryConstant.INVENTORY_READ)), +): + records = await InventoryService.get_inventory_by_product(product_id, session) + return [inventory_to_read(r) for r in records] + + +@router.post("/adjust") +async def adjust_inventory( + req: InventoryAdjustRequest, + session: AsyncSession = Depends(get_session), + current_user=Depends(get_current_user), + _: bool = Depends(PermissionChecker(InventoryConstant.INVENTORY_ADJUST)), +): + from uuid import UUID + + inv = await InventoryService.adjust_inventory( + product_id=UUID(req.product_id), + warehouse_id=UUID(req.warehouse_id), + quantity=req.quantity, + order_type="adjustment", + order_no=f"ADJ-{req.product_id[:8]}", + change_type="adjustment", + remark=req.remark, + operator=current_user.user_id, + session=session, + ) + return inventory_to_read(inv) + + +@router.get("/movements", response_model=List[InventoryMovementRead]) +async def read_movements( + product_id: Optional[str] = None, + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(InventoryConstant.INVENTORY_MOVEMENT_READ)), +): + if product_id: + from uuid import UUID + + return await InventoryService.get_movements_by_product( + UUID(product_id), offset, limit, session + ) + return await InventoryService.get_movements(offset, limit, session) diff --git a/src/chongming/app/api/routers/erp/product_router.py b/src/chongming/app/api/routers/erp/product_router.py new file mode 100644 index 0000000..6859151 --- /dev/null +++ b/src/chongming/app/api/routers/erp/product_router.py @@ -0,0 +1,70 @@ +from typing import List +from uuid import UUID + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.product import ProductCreate, ProductRead, ProductUpdate +from ....core.database import get_session +from ....constant.permission.erp.product import ProductConstant +from ....service.erp.product_service import ProductService +from ...deps import get_current_user, PermissionChecker + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/", response_model=ProductRead) +async def create_product( + data: ProductCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(ProductConstant.PRODUCT_CREATE)), +): + return await ProductService.create(data, session) + + +@router.get("/", response_model=List[ProductRead]) +async def read_products( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(ProductConstant.PRODUCT_READ)), +): + return await ProductService.get_all(offset, limit, session) + + +@router.get("/{product_id}", response_model=ProductRead) +async def read_product( + product_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(ProductConstant.PRODUCT_READ)), +): + product = await ProductService.get_by_id(product_id, session) + if not product: + raise HTTPException(status_code=404, detail="产品不存在") + return product + + +@router.patch("/{product_id}", response_model=ProductRead) +async def update_product( + product_id: UUID, + update: ProductUpdate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(ProductConstant.PRODUCT_UPDATE)), +): + product = await ProductService.update(product_id, update, session) + if not product: + raise HTTPException(status_code=404, detail="产品不存在") + return product + + +@router.delete("/{product_id}") +async def delete_product( + product_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(ProductConstant.PRODUCT_DELETE)), +): + deleted = await ProductService.delete(product_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="产品不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/purchase_inbound_router.py b/src/chongming/app/api/routers/erp/purchase_inbound_router.py new file mode 100644 index 0000000..ee56e5f --- /dev/null +++ b/src/chongming/app/api/routers/erp/purchase_inbound_router.py @@ -0,0 +1,71 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.purchase_inbound import PurchaseInboundCreate, PurchaseInboundRead, PurchaseInboundUpdate +from ....core.database import get_session +from ....constant.permission.erp.purchase_inbound import PurchaseInboundConstant +from ....service.erp.purchase_inbound_service import PurchaseInboundService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/") +async def create_purchase_inbound( + data: PurchaseInboundCreate, + session: AsyncSession = Depends(get_session), + current_user = Depends(get_current_user), + _: bool = Depends(PermissionChecker(PurchaseInboundConstant.PURCHASE_INBOUND_CREATE)), +): + return await PurchaseInboundService.create(data, current_user.user_id, session) + + +@router.get("/") +async def read_purchase_inbounds( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseInboundConstant.PURCHASE_INBOUND_READ)), +): + return await PurchaseInboundService.get_all(offset, limit, session) + + +@router.get("/{inbound_id}") +async def read_purchase_inbound( + inbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseInboundConstant.PURCHASE_INBOUND_READ)), +): + obj = await PurchaseInboundService.get_by_id(inbound_id, session) + if not obj: + raise HTTPException(status_code=404, detail="采购入库单不存在") + return obj + + +@router.post("/{inbound_id}/approve") +async def approve_purchase_inbound( + inbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseInboundConstant.PURCHASE_INBOUND_APPROVE)), +): + """审核入库单 - 同时更新库存""" + obj = await PurchaseInboundService.approve(inbound_id, session) + if not obj: + raise HTTPException(status_code=400, detail="审核失败,入库单不存在或状态不正确") + return obj + + +@router.delete("/{inbound_id}") +async def delete_purchase_inbound( + inbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseInboundConstant.PURCHASE_INBOUND_DELETE)), +): + deleted = await PurchaseInboundService.delete(inbound_id, session) + if not deleted: + raise HTTPException(status_code=400, detail="删除失败,入库单不存在或已审核完成") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/purchase_router.py b/src/chongming/app/api/routers/erp/purchase_router.py new file mode 100644 index 0000000..54fa71d --- /dev/null +++ b/src/chongming/app/api/routers/erp/purchase_router.py @@ -0,0 +1,79 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.purchase import PurchaseOrderCreate, PurchaseOrderRead, PurchaseOrderUpdate +from ....core.database import get_session +from ....constant.permission.erp.purchase import PurchaseConstant +from ....service.erp.purchase_service import PurchaseService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/") +async def create_purchase_order( + data: PurchaseOrderCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseConstant.PURCHASE_CREATE)), +): + return await PurchaseService.create(data, session) + + +@router.get("/") +async def read_purchase_orders( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseConstant.PURCHASE_READ)), +): + return await PurchaseService.get_all(offset, limit, session) + + +@router.get("/{order_id}") +async def read_purchase_order( + order_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseConstant.PURCHASE_READ)), +): + order = await PurchaseService.get_by_id(order_id, session) + if not order: + raise HTTPException(status_code=404, detail="采购订单不存在") + return order + + +@router.patch("/{order_id}/status") +async def update_purchase_order_status( + order_id: UUID, + update: PurchaseOrderUpdate, + session: AsyncSession = Depends(get_session), + current_user = Depends(get_current_user), + _: bool = Depends(PermissionChecker(PurchaseConstant.PURCHASE_UPDATE)), +): + # 如果是审核操作,需要审核权限 + if update.status in ("approved", "rejected"): + perm_checker = PermissionChecker(PurchaseConstant.PURCHASE_APPROVE) + await perm_checker(current_user=current_user, session=session) + + if update.status == "approved": + update.approved_by = current_user.user_id + + order = await PurchaseService.update_status(order_id, update, session) + if not order: + raise HTTPException(status_code=404, detail="采购订单不存在") + return order + + +@router.delete("/{order_id}") +async def delete_purchase_order( + order_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(PurchaseConstant.PURCHASE_DELETE)), +): + deleted = await PurchaseService.delete(order_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="采购订单不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/sales_outbound_router.py b/src/chongming/app/api/routers/erp/sales_outbound_router.py new file mode 100644 index 0000000..e66a660 --- /dev/null +++ b/src/chongming/app/api/routers/erp/sales_outbound_router.py @@ -0,0 +1,74 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.sales_outbound import SalesOutboundCreate, SalesOutboundRead, SalesOutboundUpdate +from ....core.database import get_session +from ....constant.permission.erp.sales_outbound import SalesOutboundConstant +from ....service.erp.sales_outbound_service import SalesOutboundService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/") +async def create_sales_outbound( + data: SalesOutboundCreate, + session: AsyncSession = Depends(get_session), + current_user = Depends(get_current_user), + _: bool = Depends(PermissionChecker(SalesOutboundConstant.SALES_OUTBOUND_CREATE)), +): + return await SalesOutboundService.create(data, current_user.user_id, session) + + +@router.get("/") +async def read_sales_outbounds( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesOutboundConstant.SALES_OUTBOUND_READ)), +): + return await SalesOutboundService.get_all(offset, limit, session) + + +@router.get("/{outbound_id}") +async def read_sales_outbound( + outbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesOutboundConstant.SALES_OUTBOUND_READ)), +): + obj = await SalesOutboundService.get_by_id(outbound_id, session) + if not obj: + raise HTTPException(status_code=404, detail="销售出库单不存在") + return obj + + +@router.post("/{outbound_id}/approve") +async def approve_sales_outbound( + outbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesOutboundConstant.SALES_OUTBOUND_APPROVE)), +): + """审核出库单 - 同时扣减库存""" + try: + obj = await SalesOutboundService.approve(outbound_id, session) + if not obj: + raise HTTPException(status_code=400, detail="审核失败,出库单不存在或状态不正确") + return obj + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + + +@router.delete("/{outbound_id}") +async def delete_sales_outbound( + outbound_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesOutboundConstant.SALES_OUTBOUND_DELETE)), +): + deleted = await SalesOutboundService.delete(outbound_id, session) + if not deleted: + raise HTTPException(status_code=400, detail="删除失败,出库单不存在或已审核完成") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/sales_router.py b/src/chongming/app/api/routers/erp/sales_router.py new file mode 100644 index 0000000..2751ed6 --- /dev/null +++ b/src/chongming/app/api/routers/erp/sales_router.py @@ -0,0 +1,78 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.sales import SalesOrderCreate, SalesOrderRead, SalesOrderUpdate +from ....core.database import get_session +from ....constant.permission.erp.sales import SalesConstant +from ....service.erp.sales_service import SalesService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/") +async def create_sales_order( + data: SalesOrderCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesConstant.SALES_CREATE)), +): + return await SalesService.create(data, session) + + +@router.get("/") +async def read_sales_orders( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesConstant.SALES_READ)), +): + return await SalesService.get_all(offset, limit, session) + + +@router.get("/{order_id}") +async def read_sales_order( + order_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesConstant.SALES_READ)), +): + order = await SalesService.get_by_id(order_id, session) + if not order: + raise HTTPException(status_code=404, detail="销售订单不存在") + return order + + +@router.patch("/{order_id}/status") +async def update_sales_order_status( + order_id: UUID, + update: SalesOrderUpdate, + session: AsyncSession = Depends(get_session), + current_user = Depends(get_current_user), + _: bool = Depends(PermissionChecker(SalesConstant.SALES_UPDATE)), +): + if update.status in ("approved", "rejected"): + perm_checker = PermissionChecker(SalesConstant.SALES_APPROVE) + await perm_checker(current_user=current_user, session=session) + + if update.status == "approved": + update.approved_by = current_user.user_id + + order = await SalesService.update_status(order_id, update, session) + if not order: + raise HTTPException(status_code=404, detail="销售订单不存在") + return order + + +@router.delete("/{order_id}") +async def delete_sales_order( + order_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SalesConstant.SALES_DELETE)), +): + deleted = await SalesService.delete(order_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="销售订单不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/supplier_router.py b/src/chongming/app/api/routers/erp/supplier_router.py new file mode 100644 index 0000000..b4a940a --- /dev/null +++ b/src/chongming/app/api/routers/erp/supplier_router.py @@ -0,0 +1,70 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.supplier import SupplierCreate, SupplierRead, SupplierUpdate +from ....core.database import get_session +from ....constant.permission.erp.supplier import SupplierConstant +from ....service.erp.supplier_service import SupplierService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/", response_model=SupplierRead) +async def create_supplier( + data: SupplierCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SupplierConstant.SUPPLIER_CREATE)), +): + return await SupplierService.create(data, session) + + +@router.get("/", response_model=List[SupplierRead]) +async def read_suppliers( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SupplierConstant.SUPPLIER_READ)), +): + return await SupplierService.get_all(offset, limit, session) + + +@router.get("/{supplier_id}", response_model=SupplierRead) +async def read_supplier( + supplier_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SupplierConstant.SUPPLIER_READ)), +): + obj = await SupplierService.get_by_id(supplier_id, session) + if not obj: + raise HTTPException(status_code=404, detail="供应商不存在") + return obj + + +@router.patch("/{supplier_id}", response_model=SupplierRead) +async def update_supplier( + supplier_id: UUID, + update: SupplierUpdate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SupplierConstant.SUPPLIER_UPDATE)), +): + obj = await SupplierService.update(supplier_id, update, session) + if not obj: + raise HTTPException(status_code=404, detail="供应商不存在") + return obj + + +@router.delete("/{supplier_id}") +async def delete_supplier( + supplier_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(SupplierConstant.SUPPLIER_DELETE)), +): + deleted = await SupplierService.delete(supplier_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="供应商不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/erp/warehouse_router.py b/src/chongming/app/api/routers/erp/warehouse_router.py new file mode 100644 index 0000000..b0d3a07 --- /dev/null +++ b/src/chongming/app/api/routers/erp/warehouse_router.py @@ -0,0 +1,70 @@ +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession + +from ....model.erp.warehouse import WarehouseCreate, WarehouseRead, WarehouseUpdate +from ....core.database import get_session +from ....constant.permission.erp.warehouse import WarehouseConstant +from ....service.erp.warehouse_service import WarehouseService +from ...deps import get_current_user, PermissionChecker +from uuid import UUID + + +router = APIRouter(dependencies=[Depends(get_current_user)]) + + +@router.post("/", response_model=WarehouseRead) +async def create_warehouse( + data: WarehouseCreate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(WarehouseConstant.WAREHOUSE_CREATE)), +): + return await WarehouseService.create(data, session) + + +@router.get("/", response_model=List[WarehouseRead]) +async def read_warehouses( + offset: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(WarehouseConstant.WAREHOUSE_READ)), +): + return await WarehouseService.get_all(offset, limit, session) + + +@router.get("/{warehouse_id}", response_model=WarehouseRead) +async def read_warehouse( + warehouse_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(WarehouseConstant.WAREHOUSE_READ)), +): + obj = await WarehouseService.get_by_id(warehouse_id, session) + if not obj: + raise HTTPException(status_code=404, detail="仓库不存在") + return obj + + +@router.patch("/{warehouse_id}", response_model=WarehouseRead) +async def update_warehouse( + warehouse_id: UUID, + update: WarehouseUpdate, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(WarehouseConstant.WAREHOUSE_UPDATE)), +): + obj = await WarehouseService.update(warehouse_id, update, session) + if not obj: + raise HTTPException(status_code=404, detail="仓库不存在") + return obj + + +@router.delete("/{warehouse_id}") +async def delete_warehouse( + warehouse_id: UUID, + session: AsyncSession = Depends(get_session), + _: bool = Depends(PermissionChecker(WarehouseConstant.WAREHOUSE_DELETE)), +): + deleted = await WarehouseService.delete(warehouse_id, session) + if not deleted: + raise HTTPException(status_code=404, detail="仓库不存在") + return {"ok": True} diff --git a/src/chongming/app/api/routers/system/__init__.py b/src/chongming/app/api/routers/system/__init__.py new file mode 100644 index 0000000..33df179 --- /dev/null +++ b/src/chongming/app/api/routers/system/__init__.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter + +from .auth_router import router as auth_router +from .permission_router import router as permission_router +from .role_router import router as role_router +from .upload_router import router as upload_router +from .code_rule_router import router as code_rule_router + +system_router = APIRouter() + +system_router.include_router(auth_router, prefix="/auth", tags=["auth"]) +system_router.include_router( + permission_router, prefix="/permission", tags=["permission"] +) +system_router.include_router(role_router, prefix="/role", tags=["role"]) +system_router.include_router(upload_router, prefix="/upload", tags=["upload"]) +system_router.include_router(code_rule_router, prefix="/code-rules", tags=["code-rules"]) diff --git a/src/chongming/app/api/routers/auth.py b/src/chongming/app/api/routers/system/auth_router.py similarity index 62% rename from src/chongming/app/api/routers/auth.py rename to src/chongming/app/api/routers/system/auth_router.py index ec24833..13b3c00 100644 --- a/src/chongming/app/api/routers/auth.py +++ b/src/chongming/app/api/routers/system/auth_router.py @@ -4,18 +4,17 @@ from fastapi import APIRouter, Depends, HTTPException, status, Request from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.ext.asyncio import AsyncSession -from ...core.database import get_session -from ...core.security import verify_password -from ...service.user import UserService -from ...service.auth import get_auth_service -from ...model.user import UserCreate, UserRead, UserLogin, Userlogout, UserSession -from ...model.http_error import HTTPError, Detail -from ..deps import get_current_user +from ....core.database import get_session +from ....core.security import verify_password +from ....service.system.user import UserService +from ....service.system.auth import get_auth_service +from ....model import UserCreate, UserRead, UserLogin, Userlogout, UserSession +from ....model import HTTPError, Detail +from ...deps import get_current_user from plugins.jwt.jwt_cache import TokenData, TokenResponse, RefreshTokenResponse - -router = APIRouter(tags=["authentication"]) +router = APIRouter() access_scheme = HTTPBearer(scheme_name="AccessToken") refresh_scheme = HTTPBearer(scheme_name="RefreshToken") @@ -252,3 +251,160 @@ async def get_current_user_info( ) return user + + +# ========== 用户管理接口(需要超级管理员权限) ========== + + +@router.get( + "/users", + response_model=dict, + summary="获取用户列表(含角色信息)", +) +async def list_users( + skip: int = 0, + limit: int = 100, + session: AsyncSession = Depends(get_session), + current_user: TokenData = Depends(get_current_user), +): + """ + 获取用户列表,返回用户信息及其关联的角色。 + 需要超级管理员权限。 + """ + users = await UserService.list_users(session=session, skip=skip, limit=limit) + total = await UserService.count_users(session=session) + + user_list = [] + for user in users: + user_list.append( + { + "id": str(user.id), + "email": user.email, + "username": user.username, + "full_name": user.full_name, + "is_active": user.is_active, + "is_superuser": user.is_superuser, + "created_at": user.created_at, + "roles": [ + {"id": r.id, "name": r.name, "description": r.description} + for r in user.roles + ], + } + ) + + return {"items": user_list, "total": total} + + +@router.get( + "/users/{user_id}", + response_model=dict, + summary="获取用户详情(含角色信息)", +) +async def get_user_detail( + user_id: UUID, + session: AsyncSession = Depends(get_session), + current_user: TokenData = Depends(get_current_user), +): + """ + 获取指定用户的详细信息,包含角色列表。 + 需要超级管理员权限。 + """ + user = await UserService.get_user_by_id(user_id, session) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=Detail(message="用户不存在", code=404).model_dump(), + ) + + return { + "id": str(user.id), + "email": user.email, + "username": user.username, + "full_name": user.full_name, + "is_active": user.is_active, + "is_superuser": user.is_superuser, + "created_at": user.created_at, + "roles": [ + {"id": r.id, "name": r.name, "description": r.description} + for r in user.roles + ], + } + + +@router.put( + "/users/{user_id}", + response_model=dict, + summary="更新用户信息", +) +async def update_user( + user_id: UUID, + user_update: dict, + session: AsyncSession = Depends(get_session), + current_user: TokenData = Depends(get_current_user), +): + """ + 更新指定用户的信息(邮箱、用户名、全名、激活状态、超级管理员状态、密码等)。 + 需要超级管理员权限。 + """ + # 验证字段 + valid_fields = { + "email", + "username", + "full_name", + "is_active", + "is_superuser", + "password", + } + update_data = { + k: v for k, v in user_update.items() if k in valid_fields and v is not None + } + + user = await UserService.update_user( + session=session, user_id=user_id, update_data=update_data + ) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=Detail(message="用户不存在", code=404).model_dump(), + ) + + return { + "id": str(user.id), + "email": user.email, + "username": user.username, + "full_name": user.full_name, + "is_active": user.is_active, + "is_superuser": user.is_superuser, + "message": "用户更新成功", + "code": 200, + } + + +@router.delete( + "/users/{user_id}", + summary="删除用户", +) +async def delete_user( + user_id: UUID, + session: AsyncSession = Depends(get_session), + current_user: TokenData = Depends(get_current_user), +): + """ + 删除指定用户。 + 需要超级管理员权限。 + """ + # 不能删除自己 + if str(user_id) == current_user.user_id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=Detail(message="不能删除自己", code=400).model_dump(), + ) + + success = await UserService.delete_user(session=session, user_id=user_id) + if not success: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=Detail(message="用户不存在", code=404).model_dump(), + ) + + return {"message": "用户删除成功", "code": 200} diff --git a/src/chongming/app/api/routers/system/code_rule_router.py b/src/chongming/app/api/routers/system/code_rule_router.py new file mode 100644 index 0000000..96ba852 --- /dev/null +++ b/src/chongming/app/api/routers/system/code_rule_router.py @@ -0,0 +1,136 @@ +""" +编码规则 API 路由 + +提供编码规则的增删改查接口,用于前端界面管理编码规则配置。 +""" +from typing import List +from uuid import UUID + +from fastapi import APIRouter, Depends, HTTPException + +from ....model.erp.code_rule import ( + CodeRule, + CodeRuleCreate, + CodeRuleUpdate, + CodeRuleRead, +) +from ....service.erp.code_rule_service import CodeRuleService +from ...deps import get_session, RequireSuperuser + +router = APIRouter() + + +@router.get("/", response_model=List[CodeRuleRead]) +async def list_code_rules( + session=Depends(get_session), + _=Depends(RequireSuperuser()), +): + """获取所有编码规则列表""" + rules = await CodeRuleService.get_all(session) + return [ + CodeRuleRead( + id=r.id, + code_type=r.code_type, + code_name=r.code_name, + prefix=r.prefix, + date_format=r.date_format, + seq_width=r.seq_width, + separator=r.separator, + is_active=r.is_active, + remark=r.remark, + created_at=r.created_at, + updated_at=r.updated_at, + ) + for r in rules + ] + + +@router.get("/{rule_id}", response_model=CodeRuleRead) +async def get_code_rule( + rule_id: UUID, + session=Depends(get_session), + _=Depends(RequireSuperuser()), +): + """获取单个编码规则""" + rule = await CodeRuleService.get_by_id(rule_id, session) + if not rule: + raise HTTPException(status_code=404, detail="编码规则不存在") + return CodeRuleRead( + id=rule.id, + code_type=rule.code_type, + code_name=rule.code_name, + prefix=rule.prefix, + date_format=rule.date_format, + seq_width=rule.seq_width, + separator=rule.separator, + is_active=rule.is_active, + remark=rule.remark, + created_at=rule.created_at, + updated_at=rule.updated_at, + ) + + +@router.post("/", response_model=CodeRuleRead) +async def create_code_rule( + data: CodeRuleCreate, + session=Depends(get_session), + _=Depends(RequireSuperuser()), +): + """创建编码规则""" + # 检查编码类型是否已存在 + existing = await CodeRuleService.get_by_code_type(data.code_type, session) + if existing: + raise HTTPException(status_code=400, detail=f"编码类型 '{data.code_type}' 已存在") + rule = await CodeRuleService.create(data, session) + return CodeRuleRead( + id=rule.id, + code_type=rule.code_type, + code_name=rule.code_name, + prefix=rule.prefix, + date_format=rule.date_format, + seq_width=rule.seq_width, + separator=rule.separator, + is_active=rule.is_active, + remark=rule.remark, + created_at=rule.created_at, + updated_at=rule.updated_at, + ) + + +@router.put("/{rule_id}", response_model=CodeRuleRead) +async def update_code_rule( + rule_id: UUID, + data: CodeRuleUpdate, + session=Depends(get_session), + _=Depends(RequireSuperuser()), +): + """更新编码规则""" + rule = await CodeRuleService.update(rule_id, data, session) + if not rule: + raise HTTPException(status_code=404, detail="编码规则不存在") + return CodeRuleRead( + id=rule.id, + code_type=rule.code_type, + code_name=rule.code_name, + prefix=rule.prefix, + date_format=rule.date_format, + seq_width=rule.seq_width, + separator=rule.separator, + is_active=rule.is_active, + remark=rule.remark, + created_at=rule.created_at, + updated_at=rule.updated_at, + ) + + +@router.delete("/{rule_id}") +async def delete_code_rule( + rule_id: UUID, + session=Depends(get_session), + _=Depends(RequireSuperuser()), +): + """删除编码规则""" + success = await CodeRuleService.delete(rule_id, session) + if not success: + raise HTTPException(status_code=404, detail="编码规则不存在") + return {"message": "删除成功"} diff --git a/src/chongming/app/api/routers/permission.py b/src/chongming/app/api/routers/system/permission_router.py similarity index 85% rename from src/chongming/app/api/routers/permission.py rename to src/chongming/app/api/routers/system/permission_router.py index 747f08e..95615ae 100644 --- a/src/chongming/app/api/routers/permission.py +++ b/src/chongming/app/api/routers/system/permission_router.py @@ -4,17 +4,16 @@ from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession -from ...core.database import get_session -from ...model.permission import Permission, PermissionCreate, PermissionUpdate -from ...model.http_error import HTTPError, Detail -from ...service.permission import PermissionService -from ..deps import get_current_user, PermissionChecker -from ...constant.permission import PermissionMgtConstant, RoleConstant +from ....core.database import get_session +from ....model import Permission, PermissionCreate, PermissionUpdate +from ....model import HTTPError, Detail +from ....service.system.permission import PermissionService +from ...deps import get_current_user, PermissionChecker +from ....constant.permission.system import PermissionMgtConstant, RoleConstant from plugins.jwt.jwt_cache import TokenData - -router = APIRouter(tags=["permission"]) +router = APIRouter() @router.post( @@ -28,7 +27,11 @@ router = APIRouter(tags=["permission"]) async def create_permission( permission_data: PermissionCreate, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_CREATE, require_superuser=True)), + _: bool = Depends( + PermissionChecker( + PermissionMgtConstant.PERMISSION_CREATE, require_superuser=True + ) + ), __: TokenData = Depends(get_current_user), ): """ @@ -78,7 +81,12 @@ async def list_permissions( limit: int = 100, resource: Optional[str] = None, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker([PermissionMgtConstant.PERMISSION_READ, RoleConstant.ROLE_READ], require_all=False)), + _: bool = Depends( + PermissionChecker( + [PermissionMgtConstant.PERMISSION_READ, RoleConstant.ROLE_READ], + require_all=False, + ) + ), __: TokenData = Depends(get_current_user), ): """ @@ -158,7 +166,11 @@ async def update_permission( permission_id: int, permission_update: PermissionUpdate, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UPDATE, require_superuser=True)), + _: bool = Depends( + PermissionChecker( + PermissionMgtConstant.PERMISSION_UPDATE, require_superuser=True + ) + ), __: TokenData = Depends(get_current_user), ): """ @@ -197,7 +209,11 @@ async def update_permission( async def delete_permission( permission_id: int, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_DELETE, require_superuser=True)), + _: bool = Depends( + PermissionChecker( + PermissionMgtConstant.PERMISSION_DELETE, require_superuser=True + ) + ), __: TokenData = Depends(get_current_user), ): """ @@ -216,7 +232,9 @@ async def delete_permission( Raises: HTTPException: 当权限被角色使用时抛出400错误,权限不存在时抛出404错误 """ - await PermissionService.delete_permission(session=session, permission_id=permission_id) + await PermissionService.delete_permission( + session=session, permission_id=permission_id + ) return {"message": "权限删除成功", "code": 200} @@ -232,7 +250,9 @@ async def assign_permission_to_role( permission_id: int, role_id: int, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -269,7 +289,11 @@ async def unbind_permission_from_role( permission_id: int, role_id: int, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker( + PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True + ) + ), __: TokenData = Depends(get_current_user), ): """ @@ -306,7 +330,9 @@ async def assign_permission_to_user( permission_id: int, user_id: UUID, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -343,7 +369,11 @@ async def unbind_permission_from_user( permission_id: int, user_id: UUID, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker( + PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True + ) + ), __: TokenData = Depends(get_current_user), ): """ diff --git a/src/chongming/app/api/routers/role.py b/src/chongming/app/api/routers/system/role_router.py similarity index 89% rename from src/chongming/app/api/routers/role.py rename to src/chongming/app/api/routers/system/role_router.py index 3f9e0e1..838d8aa 100644 --- a/src/chongming/app/api/routers/role.py +++ b/src/chongming/app/api/routers/system/role_router.py @@ -4,18 +4,17 @@ from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession -from ...core.database import get_session -from ...model.role import Role, RoleUpdate -from ...model.http_error import HTTPError, Detail -from ...service.role import RoleService -from ...service.permission import PermissionService -from ..deps import get_current_user, PermissionChecker -from ...constant.permission import RoleConstant +from ....core.database import get_session +from ....model import Role, RoleUpdate +from ....model import HTTPError, Detail +from ....service.system.role import RoleService +from ....service.system.permission import PermissionService +from ...deps import get_current_user, PermissionChecker +from ....constant.permission.system import RoleConstant from plugins.jwt.jwt_cache import TokenData - -router = APIRouter(tags=["role"]) +router = APIRouter() @router.post( @@ -31,7 +30,9 @@ async def create_role( description: Optional[str] = None, is_only_user: bool = False, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(RoleConstant.ROLE_CREATE, require_superuser=True)), + _: bool = Depends( + PermissionChecker(RoleConstant.ROLE_CREATE, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -157,7 +158,9 @@ async def update_role( role_id: int, role_update: RoleUpdate, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(RoleConstant.ROLE_UPDATE, require_superuser=True)), + _: bool = Depends( + PermissionChecker(RoleConstant.ROLE_UPDATE, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -196,7 +199,9 @@ async def update_role( async def delete_role( role_id: int, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(RoleConstant.ROLE_DELETE, require_superuser=True)), + _: bool = Depends( + PermissionChecker(RoleConstant.ROLE_DELETE, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -233,7 +238,9 @@ async def assign_role_to_user( role_id: int, user_id: UUID, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(RoleConstant.ROLE_BIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker(RoleConstant.ROLE_BIND, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -264,7 +271,8 @@ async def assign_role_to_user( ) # 检查用户是否存在 - from ...service.user import UserService + from ....service.system.user import UserService + user = await UserService.get_user_by_id(user_id, session) if not user: raise HTTPException( @@ -293,7 +301,9 @@ async def unbind_role_from_user( role_id: int, user_id: UUID, session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(RoleConstant.ROLE_UNBIND, require_superuser=True)), + _: bool = Depends( + PermissionChecker(RoleConstant.ROLE_UNBIND, require_superuser=True) + ), __: TokenData = Depends(get_current_user), ): """ @@ -348,4 +358,3 @@ async def get_role_permissions( role_id=role_id, ) return list(permissions) - diff --git a/src/chongming/app/api/routers/upload.py b/src/chongming/app/api/routers/system/upload_router.py similarity index 94% rename from src/chongming/app/api/routers/upload.py rename to src/chongming/app/api/routers/system/upload_router.py index bd6fcc5..8703408 100644 --- a/src/chongming/app/api/routers/upload.py +++ b/src/chongming/app/api/routers/system/upload_router.py @@ -3,10 +3,9 @@ import os from fastapi import APIRouter, UploadFile, File, HTTPException, Depends from pydantic import BaseModel -from ..deps import get_current_user -from ...service.uploads import UploadService -from ...core.config import get_config - +from ...deps import get_current_user +from ....service.system.uploads import UploadService +from ....core.config import get_config config = get_config() router = APIRouter(dependencies=[Depends(get_current_user)]) diff --git a/src/chongming/app/api/routers/todo.py b/src/chongming/app/api/routers/todo.py deleted file mode 100644 index 2f3aeef..0000000 --- a/src/chongming/app/api/routers/todo.py +++ /dev/null @@ -1,126 +0,0 @@ -from typing import List - -from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.ext.asyncio import AsyncSession - -from ...model.todo import TodoCreate, TodoUpdate, TodoRead -from ...core.database import get_session -from ...constant.permission import TodoConstant -from ...service.todo import TodoService -from ..deps import get_current_user, PermissionChecker - - -router = APIRouter(dependencies=[Depends(get_current_user)]) - - -# -------------------- 路由 -------------------- -@router.post("/", response_model=TodoRead) -async def create_todo( - todo: TodoCreate, - session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(TodoConstant.TODO_CREATE)), -): - """ - 创建新的待办事项 - - Args: - todo: 待办事项创建请求体,包含待办事项的详细信息 - session: 数据库会话对象,用于执行数据库操作 - - Returns: - TodoRead: 创建成功的待办事项对象,包含完整的待办事项信息 - """ - return await TodoService.create_todo(todo, session) - - -@router.get("/", response_model=List[TodoRead]) -async def read_todos( - offset: int = 0, - limit: int = 100, - session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(TodoConstant.TODO_READ)), -): - """ - 获取待办事项列表 - - Args: - offset: 分页偏移量,默认为0,用于指定从第几条记录开始查询 - limit: 每页返回的最大记录数,默认为100,用于限制返回结果的数量 - session: 数据库会话对象,用于执行数据库操作 - - Returns: - List[TodoRead]: 待办事项列表,包含指定范围内的所有待办事项 - """ - return await TodoService.get_todos(offset, limit, session) - - -@router.get("/{todo_id}", response_model=TodoRead) -async def read_todo( - todo_id: int, - session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(TodoConstant.TODO_READ)), -): - """ - 根据ID获取单个待办事项 - - Args: - todo_id: 待办事项的唯一标识符 - session: 数据库会话对象,用于执行数据库操作 - - Returns: - TodoRead: 查询到的待办事项对象 - - Raises: - HTTPException: 当待办事项不存在时抛出404错误 - """ - todo = await TodoService.get_todo_by_id(todo_id, session) - if not todo: - raise HTTPException(status_code=404, detail="Todo not found") - return todo - - -@router.patch("/{todo_id}", response_model=TodoRead) -async def update_todo( - todo_id: int, - todo_update: TodoUpdate, - session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(TodoConstant.TODO_UPDATE)), -): - """ - 更新待办事项信息 - - Args: - todo_id: 待办事项的唯一标识符 - todo_update: 待办事项更新请求体,包含需要更新的字段信息 - session: 数据库会话对象,用于执行数据库操作 - - Returns: - TodoRead: 更新后的待办事项对象,包含最新的待办事项信息 - """ - todo = await TodoService.update_todo(todo_id, todo_update, session) - return todo - - -@router.delete("/{todo_id}") -async def delete_todo( - todo_id: int, - session: AsyncSession = Depends(get_session), - _: bool = Depends(PermissionChecker(TodoConstant.TODO_DELETE)), -): - """ - 删除待办事项 - - Args: - todo_id: 待办事项的唯一标识符 - session: 数据库会话对象,用于执行数据库操作 - - Returns: - dict: 包含操作结果的字典,{"ok": True} 表示删除成功 - - Raises: - HTTPException: 当待办事项不存在时抛出404错误 - """ - is_deleted = await TodoService.delete_todo(todo_id, session) - if not is_deleted: - raise HTTPException(status_code=404, detail="Todo not found") - return {"ok": True} diff --git a/src/chongming/app/constant/permission/__init__.py b/src/chongming/app/constant/permission/__init__.py index 86c0b82..2fb5701 100644 --- a/src/chongming/app/constant/permission/__init__.py +++ b/src/chongming/app/constant/permission/__init__.py @@ -1,8 +1,7 @@ -from .personnel import PersonnelConstant -from .role import RoleConstant -from .permission_mgt import PermissionMgtConstant -from .system_config import SystemConfigConstant -from .todo import TodoConstant +from .system import SystemConstant + +# ERP 权限常量 +from .erp import ERPConstant class PermissionConstant: @@ -20,18 +19,12 @@ class PermissionConstant: ['personnel:create', 'personnel:read', ...] """ - Personnel = PersonnelConstant - PREFIX_PERSONNEL = "personnel" - Role = RoleConstant - PREFIX_ROLE = "role" - Permission = PermissionMgtConstant - PREFIX_PERMISSION = "permission" - SystemConfig = SystemConfigConstant - PREFIX_SYSTEM_CONFIG = "system_config" - PREFIX_AUDIT_LOG = "audit_log" - PREFIX_BACKUP = "backup" - Todo = TodoConstant - PREFIX_TODO = "todo" + SYSTEM = SystemConstant + PREFIX_SYSTEM = "system" + + # ERP 权限 + ERP = ERPConstant + PREFIX_ERP = "erp" @classmethod def get_all(cls): @@ -48,6 +41,17 @@ class PermissionConstant: attr = getattr(cls, attr_name) if attr_name.startswith("_") or not hasattr(attr, "__dict__"): continue + # 如果属性有 get_all 方法(如 ERPConstant),调用它 + if hasattr(attr, "get_all") and callable(getattr(attr, "get_all")): + # 避免递归调用自身 + if attr != cls: + try: + sub_perms = attr.get_all() + if isinstance(sub_perms, list): + perms.extend(sub_perms) + continue + except Exception: + pass # 遍历内部类的属性 for sub_attr in dir(attr): if sub_attr.startswith("_"): @@ -70,6 +74,18 @@ class PermissionConstant: attr = getattr(cls, attr_name) if attr_name.startswith("_") or not hasattr(attr, "__dict__"): continue + # 如果属性有 get_description_map 方法,调用它 + if hasattr(attr, "get_description_map") and callable( + getattr(attr, "get_description_map") + ): + if attr != cls: + try: + sub_map = attr.get_description_map() + if isinstance(sub_map, dict): + description_map.update(sub_map) + continue + except Exception: + pass description_dict = getattr(attr, "permission_description_dict", None) if isinstance(description_dict, dict): description_map.update(description_dict) diff --git a/src/chongming/app/constant/permission/erp/__init__.py b/src/chongming/app/constant/permission/erp/__init__.py new file mode 100644 index 0000000..9903ff6 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/__init__.py @@ -0,0 +1,66 @@ +from .product import ProductConstant +from .warehouse import WarehouseConstant +from .inventory import InventoryConstant +from .supplier import SupplierConstant +from .customer import CustomerConstant +from .purchase import PurchaseConstant +from .purchase_inbound import PurchaseInboundConstant +from .sales import SalesConstant +from .sales_outbound import SalesOutboundConstant + + +class ERPConstant: + """ERP 系统所有权限常量汇总""" + Product = ProductConstant + PREFIX_PRODUCT = "product" + + Warehouse = WarehouseConstant + PREFIX_WAREHOUSE = "warehouse" + + Inventory = InventoryConstant + PREFIX_INVENTORY = "inventory" + + Supplier = SupplierConstant + PREFIX_SUPPLIER = "supplier" + + Customer = CustomerConstant + PREFIX_CUSTOMER = "customer" + + Purchase = PurchaseConstant + PREFIX_PURCHASE = "purchase" + + PurchaseInbound = PurchaseInboundConstant + PREFIX_PURCHASE_INBOUND = "purchase_inbound" + + Sales = SalesConstant + PREFIX_SALES = "sales" + + SalesOutbound = SalesOutboundConstant + PREFIX_SALES_OUTBOUND = "sales_outbound" + + @classmethod + def get_all(cls): + perms = [] + for attr_name in dir(cls): + attr = getattr(cls, attr_name) + if attr_name.startswith("_") or not hasattr(attr, "__dict__"): + continue + for sub_attr in dir(attr): + if sub_attr.startswith("_"): + continue + val = getattr(attr, sub_attr) + if isinstance(val, str) and ":" in val: + perms.append(val) + return perms + + @classmethod + def get_description_map(cls) -> dict: + description_map = {} + for attr_name in dir(cls): + attr = getattr(cls, attr_name) + if attr_name.startswith("_") or not hasattr(attr, "__dict__"): + continue + description_dict = getattr(attr, "permission_description_dict", None) + if isinstance(description_dict, dict): + description_map.update(description_dict) + return description_map diff --git a/src/chongming/app/constant/permission/erp/customer.py b/src/chongming/app/constant/permission/erp/customer.py new file mode 100644 index 0000000..c1a3a02 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/customer.py @@ -0,0 +1,13 @@ +class CustomerConstant: + """客户管理权限常量""" + CUSTOMER_CREATE = "customer:create" + CUSTOMER_READ = "customer:read" + CUSTOMER_UPDATE = "customer:update" + CUSTOMER_DELETE = "customer:delete" + + permission_description_dict = { + CUSTOMER_CREATE: "创建客户", + CUSTOMER_READ: "查看客户", + CUSTOMER_UPDATE: "编辑客户", + CUSTOMER_DELETE: "删除客户", + } diff --git a/src/chongming/app/constant/permission/erp/inventory.py b/src/chongming/app/constant/permission/erp/inventory.py new file mode 100644 index 0000000..a85eed8 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/inventory.py @@ -0,0 +1,11 @@ +class InventoryConstant: + """库存管理权限常量""" + INVENTORY_READ = "inventory:read" + INVENTORY_ADJUST = "inventory:adjust" + INVENTORY_MOVEMENT_READ = "inventory:movement_read" + + permission_description_dict = { + INVENTORY_READ: "查看库存", + INVENTORY_ADJUST: "库存调整", + INVENTORY_MOVEMENT_READ: "查看库存变动记录", + } diff --git a/src/chongming/app/constant/permission/erp/product.py b/src/chongming/app/constant/permission/erp/product.py new file mode 100644 index 0000000..f9c4550 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/product.py @@ -0,0 +1,13 @@ +class ProductConstant: + """产品管理权限常量""" + PRODUCT_CREATE = "product:create" + PRODUCT_READ = "product:read" + PRODUCT_UPDATE = "product:update" + PRODUCT_DELETE = "product:delete" + + permission_description_dict = { + PRODUCT_CREATE: "创建产品", + PRODUCT_READ: "查看产品", + PRODUCT_UPDATE: "编辑产品", + PRODUCT_DELETE: "删除产品", + } diff --git a/src/chongming/app/constant/permission/erp/purchase.py b/src/chongming/app/constant/permission/erp/purchase.py new file mode 100644 index 0000000..884f0e0 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/purchase.py @@ -0,0 +1,15 @@ +class PurchaseConstant: + """采购订单权限常量""" + PURCHASE_CREATE = "purchase:create" + PURCHASE_READ = "purchase:read" + PURCHASE_UPDATE = "purchase:update" + PURCHASE_DELETE = "purchase:delete" + PURCHASE_APPROVE = "purchase:approve" + + permission_description_dict = { + PURCHASE_CREATE: "创建采购订单", + PURCHASE_READ: "查看采购订单", + PURCHASE_UPDATE: "编辑采购订单", + PURCHASE_DELETE: "删除采购订单", + PURCHASE_APPROVE: "审核采购订单", + } diff --git a/src/chongming/app/constant/permission/erp/purchase_inbound.py b/src/chongming/app/constant/permission/erp/purchase_inbound.py new file mode 100644 index 0000000..9a314e5 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/purchase_inbound.py @@ -0,0 +1,15 @@ +class PurchaseInboundConstant: + """采购入库单权限常量""" + PURCHASE_INBOUND_CREATE = "purchase_inbound:create" + PURCHASE_INBOUND_READ = "purchase_inbound:read" + PURCHASE_INBOUND_UPDATE = "purchase_inbound:update" + PURCHASE_INBOUND_DELETE = "purchase_inbound:delete" + PURCHASE_INBOUND_APPROVE = "purchase_inbound:approve" + + permission_description_dict = { + PURCHASE_INBOUND_CREATE: "创建采购入库单", + PURCHASE_INBOUND_READ: "查看采购入库单", + PURCHASE_INBOUND_UPDATE: "编辑采购入库单", + PURCHASE_INBOUND_DELETE: "删除采购入库单", + PURCHASE_INBOUND_APPROVE: "审核采购入库单", + } diff --git a/src/chongming/app/constant/permission/erp/sales.py b/src/chongming/app/constant/permission/erp/sales.py new file mode 100644 index 0000000..68e7f2a --- /dev/null +++ b/src/chongming/app/constant/permission/erp/sales.py @@ -0,0 +1,15 @@ +class SalesConstant: + """销售订单权限常量""" + SALES_CREATE = "sales:create" + SALES_READ = "sales:read" + SALES_UPDATE = "sales:update" + SALES_DELETE = "sales:delete" + SALES_APPROVE = "sales:approve" + + permission_description_dict = { + SALES_CREATE: "创建销售订单", + SALES_READ: "查看销售订单", + SALES_UPDATE: "编辑销售订单", + SALES_DELETE: "删除销售订单", + SALES_APPROVE: "审核销售订单", + } diff --git a/src/chongming/app/constant/permission/erp/sales_outbound.py b/src/chongming/app/constant/permission/erp/sales_outbound.py new file mode 100644 index 0000000..d9f8ff9 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/sales_outbound.py @@ -0,0 +1,15 @@ +class SalesOutboundConstant: + """销售出库单权限常量""" + SALES_OUTBOUND_CREATE = "sales_outbound:create" + SALES_OUTBOUND_READ = "sales_outbound:read" + SALES_OUTBOUND_UPDATE = "sales_outbound:update" + SALES_OUTBOUND_DELETE = "sales_outbound:delete" + SALES_OUTBOUND_APPROVE = "sales_outbound:approve" + + permission_description_dict = { + SALES_OUTBOUND_CREATE: "创建销售出库单", + SALES_OUTBOUND_READ: "查看销售出库单", + SALES_OUTBOUND_UPDATE: "编辑销售出库单", + SALES_OUTBOUND_DELETE: "删除销售出库单", + SALES_OUTBOUND_APPROVE: "审核销售出库单", + } diff --git a/src/chongming/app/constant/permission/erp/supplier.py b/src/chongming/app/constant/permission/erp/supplier.py new file mode 100644 index 0000000..ce28fab --- /dev/null +++ b/src/chongming/app/constant/permission/erp/supplier.py @@ -0,0 +1,13 @@ +class SupplierConstant: + """供应商管理权限常量""" + SUPPLIER_CREATE = "supplier:create" + SUPPLIER_READ = "supplier:read" + SUPPLIER_UPDATE = "supplier:update" + SUPPLIER_DELETE = "supplier:delete" + + permission_description_dict = { + SUPPLIER_CREATE: "创建供应商", + SUPPLIER_READ: "查看供应商", + SUPPLIER_UPDATE: "编辑供应商", + SUPPLIER_DELETE: "删除供应商", + } diff --git a/src/chongming/app/constant/permission/erp/warehouse.py b/src/chongming/app/constant/permission/erp/warehouse.py new file mode 100644 index 0000000..dd44920 --- /dev/null +++ b/src/chongming/app/constant/permission/erp/warehouse.py @@ -0,0 +1,13 @@ +class WarehouseConstant: + """仓库管理权限常量""" + WAREHOUSE_CREATE = "warehouse:create" + WAREHOUSE_READ = "warehouse:read" + WAREHOUSE_UPDATE = "warehouse:update" + WAREHOUSE_DELETE = "warehouse:delete" + + permission_description_dict = { + WAREHOUSE_CREATE: "创建仓库", + WAREHOUSE_READ: "查看仓库", + WAREHOUSE_UPDATE: "编辑仓库", + WAREHOUSE_DELETE: "删除仓库", + } diff --git a/src/chongming/app/constant/permission/system/__init__.py b/src/chongming/app/constant/permission/system/__init__.py new file mode 100644 index 0000000..41bb7af --- /dev/null +++ b/src/chongming/app/constant/permission/system/__init__.py @@ -0,0 +1,99 @@ +from .personnel import PersonnelConstant +from .role import RoleConstant +from .permission_mgt import PermissionMgtConstant +from .system_config import SystemConfigConstant + +# ERP 权限常量 + + +class SystemConstant: + """ + 权限常量类 - 按业务模块分组管理所有系统权限 + + 该类定义了系统中各个业务模块的权限标识符,采用分层结构组织: + - 每个业务域作为一个内部类(Personnel, Role, Permission, SystemConfig, Todo) + - 每个权限使用 "资源:操作" 格式定义 + + 使用示例: + >>> PermissionConstant.Personnel.PERSONNEL_CREATE + 'personnel:create' + >>> PermissionConstant.get_all() + ['personnel:create', 'personnel:read', ...] + """ + + Personnel = PersonnelConstant + PREFIX_PERSONNEL = "personnel" + Role = RoleConstant + PREFIX_ROLE = "role" + Permission = PermissionMgtConstant + PREFIX_PERMISSION = "permission" + SystemConfig = SystemConfigConstant + PREFIX_SYSTEM_CONFIG = "system_config" + PREFIX_AUDIT_LOG = "audit_log" + PREFIX_BACKUP = "backup" + PREFIX_TODO = "todo" + + @classmethod + def get_all(cls): + """ + 获取系统中定义的所有权限字符串列表 + + 通过反射机制自动收集所有内部类中定义的权限常量。 + + Returns: + list[str]: 所有权限字符串的列表 + """ + perms = [] + for attr_name in dir(cls): + attr = getattr(cls, attr_name) + if attr_name.startswith("_") or not hasattr(attr, "__dict__"): + continue + # 如果属性有 get_all 方法(如 ERPConstant),调用它 + if hasattr(attr, "get_all") and callable(getattr(attr, "get_all")): + # 避免递归调用自身 + if attr != cls: + try: + sub_perms = attr.get_all() + if isinstance(sub_perms, list): + perms.extend(sub_perms) + continue + except Exception: + pass + # 遍历内部类的属性 + for sub_attr in dir(attr): + if sub_attr.startswith("_"): + continue + val = getattr(attr, sub_attr) + if isinstance(val, str) and ":" in val: + perms.append(val) + return perms + + @classmethod + def get_description_map(cls) -> dict: + """ + 获取系统中所有权限的描述映射 + + Returns: + dict[str, str]: 权限字符串到描述的映射字典 + """ + description_map = {} + for attr_name in dir(cls): + attr = getattr(cls, attr_name) + if attr_name.startswith("_") or not hasattr(attr, "__dict__"): + continue + # 如果属性有 get_description_map 方法,调用它 + if hasattr(attr, "get_description_map") and callable( + getattr(attr, "get_description_map") + ): + if attr != cls: + try: + sub_map = attr.get_description_map() + if isinstance(sub_map, dict): + description_map.update(sub_map) + continue + except Exception: + pass + description_dict = getattr(attr, "permission_description_dict", None) + if isinstance(description_dict, dict): + description_map.update(description_dict) + return description_map diff --git a/src/chongming/app/constant/permission/permission_mgt.py b/src/chongming/app/constant/permission/system/permission_mgt.py similarity index 100% rename from src/chongming/app/constant/permission/permission_mgt.py rename to src/chongming/app/constant/permission/system/permission_mgt.py diff --git a/src/chongming/app/constant/permission/personnel.py b/src/chongming/app/constant/permission/system/personnel.py similarity index 100% rename from src/chongming/app/constant/permission/personnel.py rename to src/chongming/app/constant/permission/system/personnel.py diff --git a/src/chongming/app/constant/permission/role.py b/src/chongming/app/constant/permission/system/role.py similarity index 100% rename from src/chongming/app/constant/permission/role.py rename to src/chongming/app/constant/permission/system/role.py diff --git a/src/chongming/app/constant/permission/system_config.py b/src/chongming/app/constant/permission/system/system_config.py similarity index 100% rename from src/chongming/app/constant/permission/system_config.py rename to src/chongming/app/constant/permission/system/system_config.py diff --git a/src/chongming/app/constant/permission/todo.py b/src/chongming/app/constant/permission/todo.py deleted file mode 100644 index 99c780f..0000000 --- a/src/chongming/app/constant/permission/todo.py +++ /dev/null @@ -1,14 +0,0 @@ -class TodoConstant: - """Todo任务管理权限常量""" - - TODO_CREATE = "todo:create" # 创建待办事项 - TODO_READ = "todo:read" # 读取待办事项 - TODO_UPDATE = "todo:update" # 更新待办事项 - TODO_DELETE = "todo:delete" # 删除待办事项 - - permission_description_dict = { - TODO_CREATE: "创建待办事项", - TODO_READ: "读取待办事项", - TODO_UPDATE: "更新待办事项", - TODO_DELETE: "删除待办事项", - } diff --git a/src/chongming/app/core/order_no_generator.py b/src/chongming/app/core/order_no_generator.py new file mode 100644 index 0000000..2d842e1 --- /dev/null +++ b/src/chongming/app/core/order_no_generator.py @@ -0,0 +1,210 @@ +""" +订单号自动生成器 + +支持自定义生成规则,规则通过数据库中的 CodeRule 表配置(优先)或默认规则。 +所有编码现在都由系统自动生成,不允许手动输入。 +""" +from datetime import datetime +from typing import Optional, Dict, Any + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ..model.erp.order_no_sequence import OrderNoSequence +from ..model.erp.code_rule import CodeRule + + +# 默认编码规则(当数据库中没有配置时使用) +DEFAULT_RULES: Dict[str, Dict[str, Any]] = { + "purchase_order": { + "prefix": "PO", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "采购订单", + }, + "sales_order": { + "prefix": "SO", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "销售订单", + }, + "purchase_inbound": { + "prefix": "PI", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "采购入库单", + }, + "sales_outbound": { + "prefix": "DO", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "销售出库单", + }, + "product": { + "prefix": "PRD", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "物料", + }, + "supplier": { + "prefix": "SUP", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "供应商", + }, + "customer": { + "prefix": "CUS", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "客户", + }, + "warehouse": { + "prefix": "WH", + "date_format": "%Y%m%d", + "seq_width": 4, + "separator": "", + "code_name": "仓库", + }, +} + + +async def get_order_no_rules_from_db(session: AsyncSession) -> Dict[str, Dict[str, Any]]: + """ + 从数据库获取编码规则。 + 如果数据库中没有配置,则使用默认规则。 + """ + result = await session.execute(select(CodeRule).where(CodeRule.is_active == True)) + db_rules = list(result.scalars().all()) + + rules = {} + for rule in db_rules: + rules[rule.code_type] = { + "prefix": rule.prefix, + "date_format": rule.date_format, + "seq_width": rule.seq_width, + "separator": rule.separator, + "code_name": rule.code_name, + } + + # 补充没有在数据库中配置的订单类型使用默认规则 + for key, default_rule in DEFAULT_RULES.items(): + if key not in rules: + rules[key] = default_rule + + return rules + + +async def generate_order_no( + order_type: str, + session: AsyncSession, + custom_date: Optional[datetime] = None, +) -> str: + """ + 根据规则自动生成订单号。 + + 生成逻辑: + 1. 根据 order_type 从数据库获取对应的生成规则(前缀、日期格式、序号宽度等) + 2. 生成日期部分(如 20240508) + 3. 在数据库中查找该订单类型+日期对应的当前序号 + 4. 序号+1,更新数据库 + 5. 格式化为 {prefix}{separator}{date}{separator}{seq_padded} + + Args: + order_type: 订单类型,如 "purchase_order", "sales_order" 等 + session: 数据库会话 + custom_date: 自定义日期,默认为当前时间 + + Returns: + 生成的订单号字符串 + + Raises: + ValueError: 如果订单类型没有对应的规则配置 + """ + rules = await get_order_no_rules_from_db(session) + + if order_type not in rules: + raise ValueError(f"未找到订单类型 '{order_type}' 的订单号生成规则") + + rule = rules[order_type] + prefix = rule.get("prefix", "") + date_format = rule.get("date_format", "%Y%m%d") + seq_width = rule.get("seq_width", 4) + separator = rule.get("separator", "") + + now = custom_date or datetime.now() + date_str = now.strftime(date_format) + + # 在数据库中查找该订单类型+日期对应的序列记录 + result = await session.execute( + select(OrderNoSequence) + .where( + OrderNoSequence.order_type == order_type, + OrderNoSequence.date_str == date_str, + ) + ) + sequence = result.scalar_one_or_none() + + if sequence: + # 已存在记录,序号递增 + sequence.current_seq += 1 + else: + # 不存在记录,创建新记录,序号从1开始 + sequence = OrderNoSequence( + order_type=order_type, + date_str=date_str, + current_seq=1, + ) + + session.add(sequence) + await session.flush() + + # 格式化序号 + seq_padded = str(sequence.current_seq).zfill(seq_width) + + # 组装订单号 + if separator: + order_no = f"{prefix}{separator}{date_str}{separator}{seq_padded}" + else: + order_no = f"{prefix}{date_str}{seq_padded}" + + return order_no + + +async def generate_order_no_with_retry( + order_type: str, + session: AsyncSession, + max_retries: int = 3, + custom_date: Optional[datetime] = None, +) -> str: + """ + 生成订单号(带重试机制)。 + 在某些高并发场景下可能出现唯一性冲突,重试可以解决。 + + Args: + order_type: 订单类型 + session: 数据库会话 + max_retries: 最大重试次数 + custom_date: 自定义日期 + + Returns: + 生成的订单号 + """ + from sqlalchemy.exc import IntegrityError + + last_error = None + for attempt in range(max_retries): + try: + return await generate_order_no(order_type, session, custom_date) + except IntegrityError as e: + last_error = e + await session.rollback() + continue + + raise last_error or ValueError(f"生成订单号失败({order_type})") diff --git a/src/chongming/app/model/__init__.py b/src/chongming/app/model/__init__.py index eca2fc1..e9902fa 100644 --- a/src/chongming/app/model/__init__.py +++ b/src/chongming/app/model/__init__.py @@ -1,12 +1,33 @@ -from .user import User -from .role import Role, RoleUpdate -from .permission import Permission, PermissionUpdate, PermissionCreate -from .relationship_table import UserRole, RolePermission -from .todo import Todo +from .system.user import User, UserCreate, UserRead, UserLogin, Userlogout, UserSession, UserUpdate, UserReadWithRoles +from .system.role import Role, RoleUpdate +from .system.permission import Permission, PermissionUpdate, PermissionCreate +from .system.relationship_table import UserRole, RolePermission +from .system.http_error import HTTPError, Detail +# ERP 模型 +from .erp.product import Product, ProductCreate, ProductRead, ProductUpdate +from .erp.warehouse import Warehouse, WarehouseCreate, WarehouseRead, WarehouseUpdate +from .erp.inventory import Inventory, InventoryMovement +from .erp.supplier import Supplier, SupplierCreate, SupplierRead, SupplierUpdate +from .erp.customer import Customer, CustomerCreate, CustomerRead, CustomerUpdate +from .erp.purchase import PurchaseOrder, PurchaseOrderItem +from .erp.purchase_inbound import PurchaseInbound, PurchaseInboundItem +from .erp.sales import SalesOrder, SalesOrderItem +from .erp.sales_outbound import SalesOutbound, SalesOutboundItem + +# 订单号序列模型 +from .erp.order_no_sequence import OrderNoSequence, OrderNoSequenceCreate, OrderNoConfig +from .erp.code_rule import CodeRule, CodeRuleCreate, CodeRuleUpdate, CodeRuleRead __all__ = [ "User", + "UserCreate", + "UserRead", + "UserLogin", + "Userlogout", + "UserSession", + "UserUpdate", + "UserReadWithRoles", "Role", "RoleUpdate", "Permission", @@ -14,5 +35,41 @@ __all__ = [ "PermissionCreate", "UserRole", "RolePermission", - "Todo", + "HTTPError", + "Detail", + # ERP + "Product", + "ProductCreate", + "ProductRead", + "ProductUpdate", + "Warehouse", + "WarehouseCreate", + "WarehouseRead", + "WarehouseUpdate", + "Inventory", + "InventoryMovement", + "Supplier", + "SupplierCreate", + "SupplierRead", + "SupplierUpdate", + "Customer", + "CustomerCreate", + "CustomerRead", + "CustomerUpdate", + "PurchaseOrder", + "PurchaseOrderItem", + "PurchaseInbound", + "PurchaseInboundItem", + "SalesOrder", + "SalesOrderItem", + "SalesOutbound", + "SalesOutboundItem", + # 订单号序列 + "OrderNoSequence", + "OrderNoSequenceCreate", + "OrderNoConfig", + "CodeRule", + "CodeRuleCreate", + "CodeRuleUpdate", + "CodeRuleRead", ] diff --git a/src/chongming/app/model/erp/__init__.py b/src/chongming/app/model/erp/__init__.py new file mode 100644 index 0000000..374265f --- /dev/null +++ b/src/chongming/app/model/erp/__init__.py @@ -0,0 +1,37 @@ +from .product import Product, ProductCreate, ProductRead, ProductUpdate +from .warehouse import Warehouse, WarehouseCreate, WarehouseRead, WarehouseUpdate +from .inventory import Inventory, InventoryRead, InventoryMovement, InventoryMovementRead +from .supplier import Supplier, SupplierCreate, SupplierRead, SupplierUpdate +from .customer import Customer, CustomerCreate, CustomerRead, CustomerUpdate +from .purchase import ( + PurchaseOrder, PurchaseOrderCreate, PurchaseOrderRead, PurchaseOrderUpdate, + PurchaseOrderItem, PurchaseOrderItemCreate, PurchaseOrderItemRead, +) +from .purchase_inbound import ( + PurchaseInbound, PurchaseInboundCreate, PurchaseInboundRead, + PurchaseInboundItem, PurchaseInboundItemCreate, PurchaseInboundItemRead, +) +from .sales import ( + SalesOrder, SalesOrderCreate, SalesOrderRead, SalesOrderUpdate, + SalesOrderItem, SalesOrderItemCreate, SalesOrderItemRead, +) +from .sales_outbound import ( + SalesOutbound, SalesOutboundCreate, SalesOutboundRead, + SalesOutboundItem, SalesOutboundItemCreate, SalesOutboundItemRead, +) + +__all__ = [ + "Product", "ProductCreate", "ProductRead", "ProductUpdate", + "Warehouse", "WarehouseCreate", "WarehouseRead", "WarehouseUpdate", + "Inventory", "InventoryRead", "InventoryMovement", "InventoryMovementRead", + "Supplier", "SupplierCreate", "SupplierRead", "SupplierUpdate", + "Customer", "CustomerCreate", "CustomerRead", "CustomerUpdate", + "PurchaseOrder", "PurchaseOrderCreate", "PurchaseOrderRead", "PurchaseOrderUpdate", + "PurchaseOrderItem", "PurchaseOrderItemCreate", "PurchaseOrderItemRead", + "PurchaseInbound", "PurchaseInboundCreate", "PurchaseInboundRead", + "PurchaseInboundItem", "PurchaseInboundItemCreate", "PurchaseInboundItemRead", + "SalesOrder", "SalesOrderCreate", "SalesOrderRead", "SalesOrderUpdate", + "SalesOrderItem", "SalesOrderItemCreate", "SalesOrderItemRead", + "SalesOutbound", "SalesOutboundCreate", "SalesOutboundRead", + "SalesOutboundItem", "SalesOutboundItemCreate", "SalesOutboundItemRead", +] diff --git a/src/chongming/app/model/erp/code_rule.py b/src/chongming/app/model/erp/code_rule.py new file mode 100644 index 0000000..2692e6b --- /dev/null +++ b/src/chongming/app/model/erp/code_rule.py @@ -0,0 +1,64 @@ +""" +编码规则模型 + +用于存储和管理系统中所有编码规则(订单号、单据号等)的配置。 +支持从前端界面配置编码规则,替代传统的 config.toml 配置方式。 +""" +from typing import Optional, List +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field + + +class CodeRule(SQLModel, table=True): + """编码规则数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + code_type: str = Field(unique=True, index=True, max_length=50, description="编码类型,如 purchase_order, sales_order, purchase_inbound, sales_outbound") + code_name: str = Field(max_length=100, description="编码名称,如'采购订单'、'销售订单'") + prefix: str = Field(default="", max_length=20, description="前缀") + date_format: str = Field(default="%Y%m%d", max_length=20, description="日期格式") + seq_width: int = Field(default=4, description="序号位数(补零)") + separator: str = Field(default="", max_length=5, description="分隔符") + is_active: bool = Field(default=True, description="是否启用") + remark: Optional[str] = Field(default=None, max_length=500, description="备注") + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class CodeRuleCreate(SQLModel): + """创建编码规则请求""" + code_type: str + code_name: str + prefix: str = "" + date_format: str = "%Y%m%d" + seq_width: int = 4 + separator: str = "" + is_active: bool = True + remark: Optional[str] = None + + +class CodeRuleUpdate(SQLModel): + """更新编码规则请求""" + code_name: Optional[str] = None + prefix: Optional[str] = None + date_format: Optional[str] = None + seq_width: Optional[int] = None + separator: Optional[str] = None + is_active: Optional[bool] = None + remark: Optional[str] = None + + +class CodeRuleRead(SQLModel): + """编码规则响应模型""" + id: UUID + code_type: str + code_name: str + prefix: str + date_format: str + seq_width: int + separator: str + is_active: bool + remark: Optional[str] = None + created_at: str + updated_at: str diff --git a/src/chongming/app/model/erp/customer.py b/src/chongming/app/model/erp/customer.py new file mode 100644 index 0000000..642940a --- /dev/null +++ b/src/chongming/app/model/erp/customer.py @@ -0,0 +1,66 @@ +from typing import Optional +from uuid import uuid4, UUID + +from sqlmodel import SQLModel, Field + + +class CustomerBase(SQLModel): + """ + 客户基础模型 + + Attributes: + code: 客户编码,唯一(系统自动生成) + name: 客户名称 + contact_person: 联系人 + phone: 联系电话 + email: 邮箱 + address: 地址 + is_active: 是否启用 + remark: 备注 + """ + code: str = Field(unique=True, index=True, max_length=50) + name: str = Field(max_length=200) + contact_person: Optional[str] = Field(default=None, max_length=100) + phone: Optional[str] = Field(default=None, max_length=50) + email: Optional[str] = Field(default=None, max_length=100) + address: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + remark: Optional[str] = Field(default=None, max_length=500) + + +class Customer(CustomerBase, table=True): + """客户数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + + +class CustomerCreate(SQLModel): + """ + 创建客户请求模型 + 注意:编码由系统自动生成,不允许手动输入 + """ + name: str = Field(max_length=200) + contact_person: Optional[str] = Field(default=None, max_length=100) + phone: Optional[str] = Field(default=None, max_length=50) + email: Optional[str] = Field(default=None, max_length=100) + address: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + remark: Optional[str] = Field(default=None, max_length=500) + + +class CustomerRead(CustomerBase): + """客户响应模型""" + id: UUID + + +class CustomerUpdate(SQLModel): + """ + 更新客户请求模型 + 注意:编码不允许修改 + """ + name: Optional[str] = None + contact_person: Optional[str] = None + phone: Optional[str] = None + email: Optional[str] = None + address: Optional[str] = None + is_active: Optional[bool] = None + remark: Optional[str] = None diff --git a/src/chongming/app/model/erp/inventory.py b/src/chongming/app/model/erp/inventory.py new file mode 100644 index 0000000..6b6bffa --- /dev/null +++ b/src/chongming/app/model/erp/inventory.py @@ -0,0 +1,87 @@ +from typing import Optional +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field + + +class Inventory(SQLModel, table=True): + """ + 库存模型 + + Attributes: + id: 主键 + product_id: 产品ID + warehouse_id: 仓库ID + quantity: 当前库存数量 + locked_quantity: 锁定数量(已下单未出库) + created_at: 创建时间 + updated_at: 更新时间 + """ + id: UUID = Field(default_factory=uuid4, primary_key=True) + product_id: UUID = Field(index=True) + warehouse_id: UUID = Field(index=True) + quantity: float = Field(default=0) + locked_quantity: float = Field(default=0) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class InventoryRead(SQLModel): + """库存响应模型""" + id: UUID + product_id: UUID + warehouse_id: UUID + quantity: float + locked_quantity: float + available_quantity: float # 可用数量 = quantity - locked_quantity + created_at: str + updated_at: str + + +class InventoryMovement(SQLModel, table=True): + """ + 库存变动记录模型 + + Attributes: + id: 主键 + product_id: 产品ID + warehouse_id: 仓库ID + change_type: 变动类型 (purchase_inbound, sales_outbound, adjustment, transfer_in, transfer_out) + order_type: 单据类型 (purchase_order, sales_order, etc.) + order_no: 单据编号 + quantity: 变动数量(正数增加,负数减少) + before_quantity: 变动前数量 + after_quantity: 变动后数量 + remark: 备注 + operator: 操作人 + created_at: 创建时间 + """ + id: UUID = Field(default_factory=uuid4, primary_key=True) + product_id: UUID = Field(index=True) + warehouse_id: UUID = Field(index=True) + change_type: str = Field(max_length=50) # purchase_inbound, sales_outbound, adjustment + order_type: str = Field(max_length=50) + order_no: str = Field(max_length=100) + quantity: float + before_quantity: float = Field(default=0) + after_quantity: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + operator: Optional[str] = Field(default=None, max_length=100) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class InventoryMovementRead(SQLModel): + """库存变动响应模型""" + id: UUID + product_id: UUID + warehouse_id: UUID + change_type: str + order_type: str + order_no: str + quantity: float + before_quantity: float + after_quantity: float + remark: Optional[str] + operator: Optional[str] + created_at: str diff --git a/src/chongming/app/model/erp/order_no_sequence.py b/src/chongming/app/model/erp/order_no_sequence.py new file mode 100644 index 0000000..2f6aff4 --- /dev/null +++ b/src/chongming/app/model/erp/order_no_sequence.py @@ -0,0 +1,32 @@ +""" +订单号序列追踪模型 + +用于记录每种订单类型每天生成的序号,确保订单号唯一且连续。 +""" +from typing import Optional +from uuid import uuid4, UUID + +from sqlmodel import SQLModel, Field + + +class OrderNoSequence(SQLModel, table=True): + """订单号序列表 - 追踪每种订单类型每天的当前序号""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + order_type: str = Field(index=True, max_length=50, description="订单类型,如 purchase_order, sales_order 等") + date_str: str = Field(index=True, max_length=20, description="日期字符串,如 20240508") + current_seq: int = Field(default=0, description="当前最大序号") + + +class OrderNoSequenceCreate(SQLModel): + """创建订单号序列请求""" + order_type: str + date_str: str + current_seq: int = 0 + + +class OrderNoConfig(SQLModel): + """订单号生成规则配置""" + prefix: str = Field(default="", description="前缀") + date_format: str = Field(default="%Y%m%d", description="日期格式") + seq_width: int = Field(default=4, description="序号位数(补零)") + separator: str = Field(default="", description="分隔符,如 '-'") diff --git a/src/chongming/app/model/erp/product.py b/src/chongming/app/model/erp/product.py new file mode 100644 index 0000000..0ae39f8 --- /dev/null +++ b/src/chongming/app/model/erp/product.py @@ -0,0 +1,89 @@ +from enum import Enum +from typing import Optional +from uuid import uuid4, UUID + +from sqlmodel import SQLModel, Field + + +class MaterialCategory(str, Enum): + """物料分类枚举""" + RAW_MATERIAL = "raw_material" + FINISHED_PRODUCT = "finished_product" + AUXILIARY_MATERIAL = "auxiliary_material" + OTHER = "other" + + @property + def label(self) -> str: + labels = { + MaterialCategory.RAW_MATERIAL: "原材料", + MaterialCategory.FINISHED_PRODUCT: "产成品", + MaterialCategory.AUXILIARY_MATERIAL: "辅料", + MaterialCategory.OTHER: "其他", + } + return labels[self] + + +class ProductBase(SQLModel): + """ + 产品/商品基础模型(物料管理) + + Attributes: + code: 商品编码,唯一(系统自动生成) + name: 商品名称 + category: 物料分类(原材料/产成品/辅料/其他) + spec: 规格型号 + unit: 计量单位 + purchase_price: 采购价 + sale_price: 销售价 + description: 描述 + is_active: 是否启用 + """ + code: str = Field(unique=True, index=True, max_length=50) + name: str = Field(max_length=200) + category: Optional[MaterialCategory] = Field(default=None, max_length=50) + spec: Optional[str] = Field(default=None, max_length=200) + unit: str = Field(default="个", max_length=20) + purchase_price: float = Field(default=0) + sale_price: float = Field(default=0) + description: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + + +class Product(ProductBase, table=True): + """产品数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + + +class ProductCreate(SQLModel): + """ + 创建产品请求模型 + 注意:编码由系统自动生成,不允许手动输入 + """ + name: str = Field(max_length=200) + category: Optional[MaterialCategory] = Field(default=None, max_length=50) + spec: Optional[str] = Field(default=None, max_length=200) + unit: str = Field(default="个", max_length=20) + purchase_price: float = Field(default=0) + sale_price: float = Field(default=0) + description: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + + +class ProductRead(ProductBase): + """产品响应模型""" + id: UUID + + +class ProductUpdate(SQLModel): + """ + 更新产品请求模型 + 注意:编码不允许修改 + """ + name: Optional[str] = None + category: Optional[MaterialCategory] = None + spec: Optional[str] = None + unit: Optional[str] = None + purchase_price: Optional[float] = None + sale_price: Optional[float] = None + description: Optional[str] = None + is_active: Optional[bool] = None diff --git a/src/chongming/app/model/erp/purchase.py b/src/chongming/app/model/erp/purchase.py new file mode 100644 index 0000000..1ecd903 --- /dev/null +++ b/src/chongming/app/model/erp/purchase.py @@ -0,0 +1,89 @@ +from typing import Optional, List +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field, Relationship + + +class PurchaseOrderBase(SQLModel): + """ + 采购订单基础模型 + + Attributes: + order_no: 订单编号,唯一 + supplier_id: 供应商ID + status: 状态 (draft, pending_approval, approved, rejected, completed, closed) + total_amount: 订单总金额 + remark: 备注 + created_by: 创建人 + approved_by: 审核人 + created_at: 创建时间 + updated_at: 更新时间 + """ + order_no: str = Field(unique=True, index=True, max_length=100) + supplier_id: UUID = Field(index=True) + status: str = Field(default="draft", max_length=30) + total_amount: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + created_by: Optional[str] = Field(default=None, max_length=100) + approved_by: Optional[str] = Field(default=None, max_length=100) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class PurchaseOrder(PurchaseOrderBase, table=True): + """采购订单数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + items: List["PurchaseOrderItem"] = Relationship(back_populates="order") + + +class PurchaseOrderCreate(SQLModel): + """创建采购订单请求模型 - 订单号由系统自动生成,不允许手动输入""" + supplier_id: UUID + remark: Optional[str] = None + items: List["PurchaseOrderItemCreate"] + + +class PurchaseOrderItemCreate(SQLModel): + """创建采购订单明细请求模型""" + product_id: UUID + quantity: float + price: float + remark: Optional[str] = None + + +class PurchaseOrderItemBase(SQLModel): + """采购订单明细基础模型""" + product_id: UUID = Field(index=True) + quantity: float = Field(default=1) + price: float = Field(default=0) + amount: float = Field(default=0) + received_quantity: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + + +class PurchaseOrderItem(PurchaseOrderItemBase, table=True): + """采购订单明细数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + order_id: UUID = Field(foreign_key="purchaseorder.id") + order: Optional[PurchaseOrder] = Relationship(back_populates="items") + + +class PurchaseOrderItemRead(PurchaseOrderItemBase): + """采购订单明细响应模型""" + id: UUID + order_id: UUID + + +class PurchaseOrderRead(PurchaseOrderBase): + """采购订单响应模型""" + id: UUID + items: List[PurchaseOrderItemRead] = [] + + +class PurchaseOrderUpdate(SQLModel): + """更新采购订单请求模型""" + status: Optional[str] = None + remark: Optional[str] = None + total_amount: Optional[float] = None + approved_by: Optional[str] = None diff --git a/src/chongming/app/model/erp/purchase_inbound.py b/src/chongming/app/model/erp/purchase_inbound.py new file mode 100644 index 0000000..d88edf4 --- /dev/null +++ b/src/chongming/app/model/erp/purchase_inbound.py @@ -0,0 +1,85 @@ +from typing import Optional, List +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field, Relationship + + +class PurchaseInboundBase(SQLModel): + """ + 采购入库单基础模型 + + Attributes: + inbound_no: 入库单编号,唯一 + purchase_order_id: 关联采购订单ID + warehouse_id: 入库仓库ID + status: 状态 (draft, completed, cancelled) + total_quantity: 入库总数量 + remark: 备注 + operator: 操作人 + created_at: 创建时间 + updated_at: 更新时间 + """ + inbound_no: str = Field(unique=True, index=True, max_length=100) + purchase_order_id: UUID = Field(index=True) + warehouse_id: UUID = Field(index=True) + status: str = Field(default="draft", max_length=30) + total_quantity: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + operator: Optional[str] = Field(default=None, max_length=100) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class PurchaseInbound(PurchaseInboundBase, table=True): + """采购入库单数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + items: List["PurchaseInboundItem"] = Relationship(back_populates="inbound") + + +class PurchaseInboundCreate(SQLModel): + """创建采购入库单请求模型 - 入库单号由系统自动生成,不允许手动输入""" + purchase_order_id: UUID + warehouse_id: UUID + remark: Optional[str] = None + items: List["PurchaseInboundItemCreate"] + + +class PurchaseInboundItemCreate(SQLModel): + """创建入库明细请求模型""" + product_id: UUID + quantity: float + remark: Optional[str] = None + + +class PurchaseInboundItemBase(SQLModel): + """入库明细基础模型""" + product_id: UUID = Field(index=True) + quantity: float = Field(default=1) + remark: Optional[str] = Field(default=None, max_length=500) + + +class PurchaseInboundItem(PurchaseInboundItemBase, table=True): + """入库明细数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + inbound_id: UUID = Field(foreign_key="purchaseinbound.id") + inbound: Optional[PurchaseInbound] = Relationship(back_populates="items") + + +class PurchaseInboundItemRead(PurchaseInboundItemBase): + """入库明细响应模型""" + id: UUID + inbound_id: UUID + + +class PurchaseInboundRead(PurchaseInboundBase): + """入库单响应模型""" + id: UUID + items: List[PurchaseInboundItemRead] = [] + + +class PurchaseInboundUpdate(SQLModel): + """更新入库单请求模型""" + status: Optional[str] = None + remark: Optional[str] = None + operator: Optional[str] = None diff --git a/src/chongming/app/model/erp/sales.py b/src/chongming/app/model/erp/sales.py new file mode 100644 index 0000000..27ef774 --- /dev/null +++ b/src/chongming/app/model/erp/sales.py @@ -0,0 +1,89 @@ +from typing import Optional, List +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field, Relationship + + +class SalesOrderBase(SQLModel): + """ + 销售订单基础模型 + + Attributes: + order_no: 订单编号,唯一 + customer_id: 客户ID + status: 状态 (draft, pending_approval, approved, rejected, completed, closed) + total_amount: 订单总金额 + remark: 备注 + created_by: 创建人 + approved_by: 审核人 + created_at: 创建时间 + updated_at: 更新时间 + """ + order_no: str = Field(unique=True, index=True, max_length=100) + customer_id: UUID = Field(index=True) + status: str = Field(default="draft", max_length=30) + total_amount: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + created_by: Optional[str] = Field(default=None, max_length=100) + approved_by: Optional[str] = Field(default=None, max_length=100) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class SalesOrder(SalesOrderBase, table=True): + """销售订单数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + items: List["SalesOrderItem"] = Relationship(back_populates="order") + + +class SalesOrderCreate(SQLModel): + """创建销售订单请求模型 - 订单号由系统自动生成,不允许手动输入""" + customer_id: UUID + remark: Optional[str] = None + items: List["SalesOrderItemCreate"] + + +class SalesOrderItemCreate(SQLModel): + """创建销售订单明细请求模型""" + product_id: UUID + quantity: float + price: float + remark: Optional[str] = None + + +class SalesOrderItemBase(SQLModel): + """销售订单明细基础模型""" + product_id: UUID = Field(index=True) + quantity: float = Field(default=1) + price: float = Field(default=0) + amount: float = Field(default=0) + delivered_quantity: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + + +class SalesOrderItem(SalesOrderItemBase, table=True): + """销售订单明细数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + order_id: UUID = Field(foreign_key="salesorder.id") + order: Optional[SalesOrder] = Relationship(back_populates="items") + + +class SalesOrderItemRead(SalesOrderItemBase): + """销售订单明细响应模型""" + id: UUID + order_id: UUID + + +class SalesOrderRead(SalesOrderBase): + """销售订单响应模型""" + id: UUID + items: List[SalesOrderItemRead] = [] + + +class SalesOrderUpdate(SQLModel): + """更新销售订单请求模型""" + status: Optional[str] = None + remark: Optional[str] = None + total_amount: Optional[float] = None + approved_by: Optional[str] = None diff --git a/src/chongming/app/model/erp/sales_outbound.py b/src/chongming/app/model/erp/sales_outbound.py new file mode 100644 index 0000000..2954779 --- /dev/null +++ b/src/chongming/app/model/erp/sales_outbound.py @@ -0,0 +1,85 @@ +from typing import Optional, List +from uuid import uuid4, UUID +from datetime import datetime + +from sqlmodel import SQLModel, Field, Relationship + + +class SalesOutboundBase(SQLModel): + """ + 销售出库单基础模型 + + Attributes: + outbound_no: 出库单编号,唯一 + sales_order_id: 关联销售订单ID + warehouse_id: 出库仓库ID + status: 状态 (draft, completed, cancelled) + total_quantity: 出库总数量 + remark: 备注 + operator: 操作人 + created_at: 创建时间 + updated_at: 更新时间 + """ + outbound_no: str = Field(unique=True, index=True, max_length=100) + sales_order_id: UUID = Field(index=True) + warehouse_id: UUID = Field(index=True) + status: str = Field(default="draft", max_length=30) + total_quantity: float = Field(default=0) + remark: Optional[str] = Field(default=None, max_length=500) + operator: Optional[str] = Field(default=None, max_length=100) + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + updated_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + + +class SalesOutbound(SalesOutboundBase, table=True): + """销售出库单数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + items: List["SalesOutboundItem"] = Relationship(back_populates="outbound") + + +class SalesOutboundCreate(SQLModel): + """创建销售出库单请求模型 - 出库单号由系统自动生成,不允许手动输入""" + sales_order_id: UUID + warehouse_id: UUID + remark: Optional[str] = None + items: List["SalesOutboundItemCreate"] + + +class SalesOutboundItemCreate(SQLModel): + """创建出库明细请求模型""" + product_id: UUID + quantity: float + remark: Optional[str] = None + + +class SalesOutboundItemBase(SQLModel): + """出库明细基础模型""" + product_id: UUID = Field(index=True) + quantity: float = Field(default=1) + remark: Optional[str] = Field(default=None, max_length=500) + + +class SalesOutboundItem(SalesOutboundItemBase, table=True): + """出库明细数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + outbound_id: UUID = Field(foreign_key="salesoutbound.id") + outbound: Optional[SalesOutbound] = Relationship(back_populates="items") + + +class SalesOutboundItemRead(SalesOutboundItemBase): + """出库明细响应模型""" + id: UUID + outbound_id: UUID + + +class SalesOutboundRead(SalesOutboundBase): + """出库单响应模型""" + id: UUID + items: List[SalesOutboundItemRead] = [] + + +class SalesOutboundUpdate(SQLModel): + """更新出库单请求模型""" + status: Optional[str] = None + remark: Optional[str] = None + operator: Optional[str] = None diff --git a/src/chongming/app/model/erp/supplier.py b/src/chongming/app/model/erp/supplier.py new file mode 100644 index 0000000..3798927 --- /dev/null +++ b/src/chongming/app/model/erp/supplier.py @@ -0,0 +1,66 @@ +from typing import Optional +from uuid import uuid4, UUID + +from sqlmodel import SQLModel, Field + + +class SupplierBase(SQLModel): + """ + 供应商基础模型 + + Attributes: + code: 供应商编码,唯一(系统自动生成) + name: 供应商名称 + contact_person: 联系人 + phone: 联系电话 + email: 邮箱 + address: 地址 + is_active: 是否启用 + remark: 备注 + """ + code: str = Field(unique=True, index=True, max_length=50) + name: str = Field(max_length=200) + contact_person: Optional[str] = Field(default=None, max_length=100) + phone: Optional[str] = Field(default=None, max_length=50) + email: Optional[str] = Field(default=None, max_length=100) + address: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + remark: Optional[str] = Field(default=None, max_length=500) + + +class Supplier(SupplierBase, table=True): + """供应商数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + + +class SupplierCreate(SQLModel): + """ + 创建供应商请求模型 + 注意:编码由系统自动生成,不允许手动输入 + """ + name: str = Field(max_length=200) + contact_person: Optional[str] = Field(default=None, max_length=100) + phone: Optional[str] = Field(default=None, max_length=50) + email: Optional[str] = Field(default=None, max_length=100) + address: Optional[str] = Field(default=None, max_length=500) + is_active: bool = Field(default=True) + remark: Optional[str] = Field(default=None, max_length=500) + + +class SupplierRead(SupplierBase): + """供应商响应模型""" + id: UUID + + +class SupplierUpdate(SQLModel): + """ + 更新供应商请求模型 + 注意:编码不允许修改 + """ + name: Optional[str] = None + contact_person: Optional[str] = None + phone: Optional[str] = None + email: Optional[str] = None + address: Optional[str] = None + is_active: Optional[bool] = None + remark: Optional[str] = None diff --git a/src/chongming/app/model/erp/warehouse.py b/src/chongming/app/model/erp/warehouse.py new file mode 100644 index 0000000..2079b2c --- /dev/null +++ b/src/chongming/app/model/erp/warehouse.py @@ -0,0 +1,58 @@ +from typing import Optional +from uuid import uuid4, UUID + +from sqlmodel import SQLModel, Field + + +class WarehouseBase(SQLModel): + """ + 仓库基础模型 + + Attributes: + code: 仓库编码,唯一(系统自动生成) + name: 仓库名称 + address: 仓库地址 + manager: 仓库管理员 + is_active: 是否启用 + description: 描述 + """ + code: str = Field(unique=True, index=True, max_length=50) + name: str = Field(max_length=200) + address: Optional[str] = Field(default=None, max_length=500) + manager: Optional[str] = Field(default=None, max_length=100) + is_active: bool = Field(default=True) + description: Optional[str] = Field(default=None, max_length=500) + + +class Warehouse(WarehouseBase, table=True): + """仓库数据库模型""" + id: UUID = Field(default_factory=uuid4, primary_key=True) + + +class WarehouseCreate(SQLModel): + """ + 创建仓库请求模型 + 注意:编码由系统自动生成,不允许手动输入 + """ + name: str = Field(max_length=200) + address: Optional[str] = Field(default=None, max_length=500) + manager: Optional[str] = Field(default=None, max_length=100) + is_active: bool = Field(default=True) + description: Optional[str] = Field(default=None, max_length=500) + + +class WarehouseRead(WarehouseBase): + """仓库响应模型""" + id: UUID + + +class WarehouseUpdate(SQLModel): + """ + 更新仓库请求模型 + 注意:编码不允许修改 + """ + name: Optional[str] = None + address: Optional[str] = None + manager: Optional[str] = None + is_active: Optional[bool] = None + description: Optional[str] = None diff --git a/src/chongming/app/model/system/__init__.py b/src/chongming/app/model/system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/chongming/app/model/http_error.py b/src/chongming/app/model/system/http_error.py similarity index 100% rename from src/chongming/app/model/http_error.py rename to src/chongming/app/model/system/http_error.py diff --git a/src/chongming/app/model/permission.py b/src/chongming/app/model/system/permission.py similarity index 100% rename from src/chongming/app/model/permission.py rename to src/chongming/app/model/system/permission.py diff --git a/src/chongming/app/model/relationship_table.py b/src/chongming/app/model/system/relationship_table.py similarity index 100% rename from src/chongming/app/model/relationship_table.py rename to src/chongming/app/model/system/relationship_table.py diff --git a/src/chongming/app/model/role.py b/src/chongming/app/model/system/role.py similarity index 100% rename from src/chongming/app/model/role.py rename to src/chongming/app/model/system/role.py diff --git a/src/chongming/app/model/user.py b/src/chongming/app/model/system/user.py similarity index 74% rename from src/chongming/app/model/user.py rename to src/chongming/app/model/system/user.py index 4b15444..2170440 100644 --- a/src/chongming/app/model/user.py +++ b/src/chongming/app/model/system/user.py @@ -125,3 +125,47 @@ class UserSession(BaseModel): user_id: str sessions: List[Dict[str, Optional[str]]] count: int + +class UserUpdate(BaseModel): + """ + 用户更新请求模型 + + 用于更新用户信息时的数据验证和传输。 + 所有字段均为可选,允许部分更新。 + + Attributes: + email: 用户邮箱地址 + username: 用户名 + full_name: 用户全名 + is_active: 用户激活状态 + is_superuser: 是否为超级管理员 + password: 用户新密码(如需修改密码) + """ + + email: Optional[str] = None + username: Optional[str] = None + full_name: Optional[str] = None + is_active: Optional[bool] = None + is_superuser: Optional[bool] = None + password: Optional[str] = None + + +class UserReadWithRoles(UserRead): + """ + 用户读取响应模型(包含角色信息) + + 用于向客户端返回用户信息,同时包含用户关联的角色列表。 + + Attributes: + roles: 用户关联的角色列表 + """ + + roles: List["Role"] = [] + + class Config: + from_attributes = True + + +# 延迟导入 Role 避免循环引用 +from .role import Role +UserReadWithRoles.model_rebuild() diff --git a/src/chongming/app/model/todo.py b/src/chongming/app/model/todo.py deleted file mode 100644 index 7fa9b3c..0000000 --- a/src/chongming/app/model/todo.py +++ /dev/null @@ -1,75 +0,0 @@ -from typing import Optional - -from sqlmodel import SQLModel, Field - - -# 定义模型 -class TodoBase(SQLModel): - """ - Todo任务的基础模型类,包含任务的通用字段。 - - Attributes: - title: 任务标题,必填字段 - description: 任务描述,可选字段,默认为None - completed: 任务完成状态,布尔值,默认为False - """ - - title: str - description: Optional[str] = None - completed: bool = False - - -class Todo(TodoBase, table=True): - """ - Todo任务的数据库表模型类,继承自TodoBase。 - - 该类对应数据库中的todo表,用于存储和查询任务数据。 - - Attributes: - id: 任务唯一标识符,主键,由数据库自动生成 - """ - - id: Optional[int] = Field(default=None, primary_key=True) - - -class TodoCreate(TodoBase): - """ - 创建Todo任务时使用的请求模型类。 - - 继承自TodoBase,用于接收前端传入的新建任务数据。 - 不包含id字段,因为id由数据库自动生成。 - """ - - pass - - -class TodoRead(TodoBase): - """ - 读取Todo任务时使用的响应模型类。 - - 继承自TodoBase,用于向客户端返回任务数据。 - 包含完整的任务信息,包括自动生成的id字段。 - - Attributes: - id: 任务唯一标识符,整数类型,必填字段 - """ - - id: int - - -class TodoUpdate(SQLModel): - """ - 更新Todo任务时使用的请求模型类。 - - 所有字段均为可选,允许部分更新任务信息。 - 只有提供的字段会被更新,未提供的字段保持原值。 - - Attributes: - title: 任务标题,可选字段 - description: 任务描述,可选字段 - completed: 任务完成状态,可选字段 - """ - - title: Optional[str] = None - description: Optional[str] = None - completed: Optional[bool] = None diff --git a/src/chongming/app/service/erp/__init__.py b/src/chongming/app/service/erp/__init__.py new file mode 100644 index 0000000..102ba96 --- /dev/null +++ b/src/chongming/app/service/erp/__init__.py @@ -0,0 +1,21 @@ +from .product_service import ProductService +from .warehouse_service import WarehouseService +from .inventory_service import InventoryService +from .supplier_service import SupplierService +from .customer_service import CustomerService +from .purchase_service import PurchaseService +from .purchase_inbound_service import PurchaseInboundService +from .sales_service import SalesService +from .sales_outbound_service import SalesOutboundService + +__all__ = [ + "ProductService", + "WarehouseService", + "InventoryService", + "SupplierService", + "CustomerService", + "PurchaseService", + "PurchaseInboundService", + "SalesService", + "SalesOutboundService", +] diff --git a/src/chongming/app/service/erp/code_rule_service.py b/src/chongming/app/service/erp/code_rule_service.py new file mode 100644 index 0000000..3279559 --- /dev/null +++ b/src/chongming/app/service/erp/code_rule_service.py @@ -0,0 +1,113 @@ +""" +编码规则服务类 + +管理系统中所有编码规则(订单号、单据号等)的 CRUD 操作。 +""" +from typing import Optional, List +from datetime import datetime +from uuid import UUID + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.code_rule import ( + CodeRule, + CodeRuleCreate, + CodeRuleUpdate, + CodeRuleRead, +) + + +class CodeRuleService: + """编码规则服务""" + + @staticmethod + async def get_all(session: AsyncSession) -> List[CodeRule]: + """获取所有编码规则""" + result = await session.execute( + select(CodeRule).order_by(CodeRule.code_type) + ) + return list(result.scalars().all()) + + @staticmethod + async def get_by_id(rule_id: UUID, session: AsyncSession) -> Optional[CodeRule]: + """根据ID获取编码规则""" + result = await session.execute( + select(CodeRule).where(CodeRule.id == rule_id) + ) + return result.scalar_one_or_none() + + @staticmethod + async def get_by_code_type(code_type: str, session: AsyncSession) -> Optional[CodeRule]: + """根据编码类型获取规则""" + result = await session.execute( + select(CodeRule).where(CodeRule.code_type == code_type) + ) + return result.scalar_one_or_none() + + @staticmethod + async def create(data: CodeRuleCreate, session: AsyncSession) -> CodeRule: + """创建编码规则""" + now = datetime.now().isoformat() + rule = CodeRule( + code_type=data.code_type, + code_name=data.code_name, + prefix=data.prefix, + date_format=data.date_format, + seq_width=data.seq_width, + separator=data.separator, + is_active=data.is_active, + remark=data.remark, + created_at=now, + updated_at=now, + ) + session.add(rule) + await session.commit() + await session.refresh(rule) + return rule + + @staticmethod + async def update(rule_id: UUID, data: CodeRuleUpdate, session: AsyncSession) -> Optional[CodeRule]: + """更新编码规则""" + rule = await CodeRuleService.get_by_id(rule_id, session) + if not rule: + return None + update_data = data.model_dump(exclude_unset=True) + update_data["updated_at"] = datetime.now().isoformat() + for key, value in update_data.items(): + setattr(rule, key, value) + session.add(rule) + await session.commit() + await session.refresh(rule) + return rule + + @staticmethod + async def delete(rule_id: UUID, session: AsyncSession) -> bool: + """删除编码规则""" + rule = await CodeRuleService.get_by_id(rule_id, session) + if not rule: + return False + await session.delete(rule) + await session.commit() + return True + + @staticmethod + async def init_default_rules(session: AsyncSession): + """初始化默认编码规则(如果不存在)""" + default_rules = [ + {"code_type": "purchase_order", "code_name": "采购订单", "prefix": "PO", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "sales_order", "code_name": "销售订单", "prefix": "SO", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "purchase_inbound", "code_name": "采购入库单", "prefix": "PI", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "sales_outbound", "code_name": "销售出库单", "prefix": "DO", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "product", "code_name": "物料", "prefix": "PRD", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "supplier", "code_name": "供应商", "prefix": "SUP", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "customer", "code_name": "客户", "prefix": "CUS", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + {"code_type": "warehouse", "code_name": "仓库", "prefix": "WH", "date_format": "%Y%m%d", "seq_width": 4, "separator": ""}, + ] + for rule_data in default_rules: + existing = await CodeRuleService.get_by_code_type(rule_data["code_type"], session) + if not existing: + await CodeRuleService.create( + CodeRuleCreate(**rule_data), + session + ) diff --git a/src/chongming/app/service/erp/customer_service.py b/src/chongming/app/service/erp/customer_service.py new file mode 100644 index 0000000..74a8ea2 --- /dev/null +++ b/src/chongming/app/service/erp/customer_service.py @@ -0,0 +1,64 @@ +from typing import Sequence, Optional + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.customer import Customer, CustomerCreate, CustomerUpdate +from ...core.order_no_generator import generate_order_no_with_retry + + +class CustomerService: + """客户服务类""" + + @staticmethod + async def create(data: CustomerCreate, session: AsyncSession) -> Customer: + # 自动生成客户编码(系统自动生成,不允许手动输入) + code = await generate_order_no_with_retry( + order_type="customer", + session=session, + ) + db_obj = Customer( + code=code, + name=data.name, + contact_person=data.contact_person, + phone=data.phone, + email=data.email, + address=data.address, + is_active=data.is_active, + remark=data.remark, + ) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> Sequence[Customer]: + result = await session.execute(select(Customer).offset(offset).limit(limit)) + return result.scalars().all() + + @staticmethod + async def get_by_id(obj_id, session: AsyncSession) -> Optional[Customer]: + return await session.get(Customer, obj_id) + + @staticmethod + async def update(obj_id, update: CustomerUpdate, session: AsyncSession) -> Optional[Customer]: + db_obj = await session.get(Customer, obj_id) + if not db_obj: + return None + update_data = update.model_dump(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_obj, key, value) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def delete(obj_id, session: AsyncSession) -> bool: + db_obj = await session.get(Customer, obj_id) + if not db_obj: + return False + await session.delete(db_obj) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/inventory_service.py b/src/chongming/app/service/erp/inventory_service.py new file mode 100644 index 0000000..1269830 --- /dev/null +++ b/src/chongming/app/service/erp/inventory_service.py @@ -0,0 +1,135 @@ +from typing import Sequence, Optional +from datetime import datetime + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select, and_ + +from ...model.erp.inventory import Inventory, InventoryMovement, InventoryMovementRead + + +class InventoryService: + """库存服务类""" + + @staticmethod + async def get_inventory_by_product_and_warehouse( + product_id, warehouse_id, session: AsyncSession + ) -> Optional[Inventory]: + result = await session.execute( + select(Inventory).where( + and_( + Inventory.product_id == product_id, + Inventory.warehouse_id == warehouse_id, + ) + ) + ) + return result.scalar_one_or_none() + + @staticmethod + async def get_all_inventory( + offset: int, limit: int, session: AsyncSession + ) -> Sequence[Inventory]: + result = await session.execute(select(Inventory).offset(offset).limit(limit)) + return result.scalars().all() + + @staticmethod + async def get_inventory_by_warehouse( + warehouse_id, offset: int, limit: int, session: AsyncSession + ) -> Sequence[Inventory]: + result = await session.execute( + select(Inventory) + .where(Inventory.warehouse_id == warehouse_id) + .offset(offset) + .limit(limit) + ) + return result.scalars().all() + + @staticmethod + async def get_inventory_by_product( + product_id, session: AsyncSession + ) -> Sequence[Inventory]: + result = await session.execute( + select(Inventory).where(Inventory.product_id == product_id) + ) + return result.scalars().all() + + @staticmethod + async def adjust_inventory( + product_id, warehouse_id, quantity: float, + order_type: str, order_no: str, change_type: str, + remark: Optional[str], operator: Optional[str], + session: AsyncSession, + ) -> Inventory: + """调整库存(正数增加,负数减少)""" + inv = await InventoryService.get_inventory_by_product_and_warehouse( + product_id, warehouse_id, session + ) + now = datetime.now().isoformat() + + if inv: + before_qty = inv.quantity + inv.quantity += quantity + if inv.quantity < 0: + inv.quantity = 0 + inv.updated_at = now + session.add(inv) + await session.commit() + await session.refresh(inv) + after_qty = inv.quantity + else: + before_qty = 0 + inv = Inventory( + product_id=product_id, + warehouse_id=warehouse_id, + quantity=max(0, quantity), + locked_quantity=0, + created_at=now, + updated_at=now, + ) + session.add(inv) + await session.commit() + await session.refresh(inv) + after_qty = inv.quantity + + # 记录库存变动 + movement = InventoryMovement( + product_id=product_id, + warehouse_id=warehouse_id, + change_type=change_type, + order_type=order_type, + order_no=order_no, + quantity=quantity, + before_quantity=before_qty, + after_quantity=after_qty, + remark=remark, + operator=operator, + created_at=now, + ) + session.add(movement) + await session.commit() + + return inv + + @staticmethod + async def get_movements( + offset: int, limit: int, session: AsyncSession + ) -> Sequence[InventoryMovement]: + result = await session.execute( + select(InventoryMovement) + .order_by(InventoryMovement.created_at.desc()) + .offset(offset) + .limit(limit) + ) + return result.scalars().all() + + @staticmethod + async def get_movements_by_product( + product_id, offset: int, limit: int, session: AsyncSession + ) -> Sequence[InventoryMovement]: + result = await session.execute( + select(InventoryMovement) + .where(InventoryMovement.product_id == product_id) + .order_by(InventoryMovement.created_at.desc()) + .offset(offset) + .limit(limit) + ) + return result.scalars().all() diff --git a/src/chongming/app/service/erp/product_service.py b/src/chongming/app/service/erp/product_service.py new file mode 100644 index 0000000..3a26659 --- /dev/null +++ b/src/chongming/app/service/erp/product_service.py @@ -0,0 +1,65 @@ +from typing import Sequence, Optional + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.product import Product, ProductCreate, ProductUpdate +from ...core.order_no_generator import generate_order_no_with_retry + + +class ProductService: + """产品服务类""" + + @staticmethod + async def create(data: ProductCreate, session: AsyncSession) -> Product: + # 自动生成物料编码(系统自动生成,不允许手动输入) + code = await generate_order_no_with_retry( + order_type="product", + session=session, + ) + db_obj = Product( + code=code, + name=data.name, + category=data.category, + spec=data.spec, + unit=data.unit, + purchase_price=data.purchase_price, + sale_price=data.sale_price, + description=data.description, + is_active=data.is_active, + ) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> Sequence[Product]: + result = await session.execute(select(Product).offset(offset).limit(limit)) + return result.scalars().all() + + @staticmethod + async def get_by_id(product_id, session: AsyncSession) -> Optional[Product]: + return await session.get(Product, product_id) + + @staticmethod + async def update(product_id, update: ProductUpdate, session: AsyncSession) -> Optional[Product]: + db_obj = await session.get(Product, product_id) + if not db_obj: + return None + update_data = update.model_dump(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_obj, key, value) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def delete(product_id, session: AsyncSession) -> bool: + db_obj = await session.get(Product, product_id) + if not db_obj: + return False + await session.delete(db_obj) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/purchase_inbound_service.py b/src/chongming/app/service/erp/purchase_inbound_service.py new file mode 100644 index 0000000..8d02aa8 --- /dev/null +++ b/src/chongming/app/service/erp/purchase_inbound_service.py @@ -0,0 +1,192 @@ +from typing import Sequence, Optional +from datetime import datetime +from uuid import UUID + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.purchase_inbound import ( + PurchaseInbound, PurchaseInboundCreate, PurchaseInboundUpdate, + PurchaseInboundItem, PurchaseInboundItemCreate, +) +from ...model.erp.purchase import PurchaseOrder, PurchaseOrderItem +from ...core.order_no_generator import generate_order_no_with_retry +from .inventory_service import InventoryService + + +def _to_uuid(val): + from uuid import UUID + return UUID(val) if isinstance(val, str) else val + + +class PurchaseInboundService: + """采购入库单服务类""" + + @staticmethod + def _to_dict(inbound, items=None): + if not inbound: + return None + return { + "id": str(inbound.id), + "inbound_no": inbound.inbound_no, + "purchase_order_id": str(inbound.purchase_order_id), + "warehouse_id": str(inbound.warehouse_id), + "status": inbound.status, + "total_quantity": inbound.total_quantity, + "remark": inbound.remark, + "operator": inbound.operator, + "created_at": inbound.created_at, + "updated_at": inbound.updated_at, + "items": [ + { + "id": str(item.id), + "inbound_id": str(item.inbound_id), + "product_id": str(item.product_id), + "quantity": item.quantity, + "remark": item.remark, + } + for item in (items or []) + ] if items else [], + } + + @staticmethod + async def create( + data: PurchaseInboundCreate, operator: Optional[str], session: AsyncSession + ) -> dict: + now = datetime.now().isoformat() + + # 自动生成入库单号(系统自动生成,不允许手动输入) + inbound_no = await generate_order_no_with_retry( + order_type="purchase_inbound", + session=session, + ) + + inbound = PurchaseInbound( + inbound_no=inbound_no, + purchase_order_id=data.purchase_order_id, + warehouse_id=data.warehouse_id, + status="draft", + total_quantity=0, + remark=data.remark, + operator=operator, + created_at=now, + updated_at=now, + ) + session.add(inbound) + await session.flush() + + items = [] + total_qty = 0 + for item_data in data.items: + total_qty += item_data.quantity + item = PurchaseInboundItem( + product_id=item_data.product_id, + quantity=item_data.quantity, + remark=item_data.remark, + inbound_id=inbound.id, + ) + session.add(item) + items.append(item) + + inbound.total_quantity = total_qty + session.add(inbound) + await session.commit() + return PurchaseInboundService._to_dict(inbound, items) + + @staticmethod + async def approve(inbound_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(PurchaseInbound).where(PurchaseInbound.id == _to_uuid(inbound_id)) + ) + inbound = result.scalar_one_or_none() + if not inbound or inbound.status != "draft": + return None + + inbound.status = "completed" + inbound.updated_at = datetime.now().isoformat() + session.add(inbound) + await session.commit() + + # 更新库存 + items_result = await session.execute( + select(PurchaseInboundItem).where( + PurchaseInboundItem.inbound_id == _to_uuid(inbound_id) + ) + ) + items = list(items_result.scalars().all()) + for item in items: + await InventoryService.adjust_inventory( + product_id=item.product_id, + warehouse_id=inbound.warehouse_id, + quantity=item.quantity, + order_type="purchase_inbound", + order_no=inbound.inbound_no, + change_type="purchase_inbound", + remark="采购入库", + operator=inbound.operator, + session=session, + ) + + # 更新采购订单的收货数量 + po_items = await session.execute( + select(PurchaseOrderItem).where( + PurchaseOrderItem.order_id == inbound.purchase_order_id, + PurchaseOrderItem.product_id == item.product_id, + ) + ) + for po_item in po_items.scalars().all(): + po_item.received_quantity = (po_item.received_quantity or 0) + item.quantity + session.add(po_item) + + await session.commit() + return PurchaseInboundService._to_dict(inbound, items) + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> list: + result = await session.execute( + select(PurchaseInbound).offset(offset).limit(limit) + ) + inbounds = list(result.scalars().all()) + result_list = [] + for inbound in inbounds: + items_result = await session.execute( + select(PurchaseInboundItem).where(PurchaseInboundItem.inbound_id == inbound.id) + ) + items = list(items_result.scalars().all()) + result_list.append(PurchaseInboundService._to_dict(inbound, items)) + return result_list + + @staticmethod + async def get_by_id(inbound_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(PurchaseInbound).where(PurchaseInbound.id == _to_uuid(inbound_id)) + ) + inbound = result.scalar_one_or_none() + if inbound: + items_result = await session.execute( + select(PurchaseInboundItem).where(PurchaseInboundItem.inbound_id == inbound.id) + ) + items = list(items_result.scalars().all()) + return PurchaseInboundService._to_dict(inbound, items) + return None + + @staticmethod + async def delete(inbound_id, session: AsyncSession) -> bool: + result = await session.execute( + select(PurchaseInbound).where(PurchaseInbound.id == _to_uuid(inbound_id)) + ) + inbound = result.scalar_one_or_none() + if not inbound: + return False + if inbound.status == "completed": + return False + items_result = await session.execute( + select(PurchaseInboundItem).where( + PurchaseInboundItem.inbound_id == _to_uuid(inbound_id) + ) + ) + for item in items_result.scalars().all(): + await session.delete(item) + await session.delete(inbound) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/purchase_service.py b/src/chongming/app/service/erp/purchase_service.py new file mode 100644 index 0000000..a60b6bf --- /dev/null +++ b/src/chongming/app/service/erp/purchase_service.py @@ -0,0 +1,165 @@ +from typing import Sequence, Optional +from datetime import datetime +from uuid import UUID + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.purchase import ( + PurchaseOrder, PurchaseOrderCreate, PurchaseOrderUpdate, + PurchaseOrderItem, PurchaseOrderItemCreate, +) +from ...core.order_no_generator import generate_order_no_with_retry + + +def _to_uuid(val): + from uuid import UUID + return UUID(val) if isinstance(val, str) else val + + +class PurchaseService: + """采购订单服务类""" + + @staticmethod + def _order_to_dict(order, items=None): + if not order: + return None + return { + "id": str(order.id), + "order_no": order.order_no, + "supplier_id": str(order.supplier_id), + "status": order.status, + "total_amount": order.total_amount, + "remark": order.remark, + "created_by": order.created_by, + "approved_by": order.approved_by, + "created_at": order.created_at, + "updated_at": order.updated_at, + "items": [ + { + "id": str(item.id), + "order_id": str(item.order_id), + "product_id": str(item.product_id), + "quantity": item.quantity, + "price": item.price, + "amount": item.amount, + "received_quantity": item.received_quantity, + "remark": item.remark, + } + for item in (items or []) + ] if items else [], + } + + @staticmethod + async def create(data: PurchaseOrderCreate, session: AsyncSession) -> dict: + now = datetime.now().isoformat() + + # 自动生成订单号(系统自动生成,不允许手动输入) + order_no = await generate_order_no_with_retry( + order_type="purchase_order", + session=session, + ) + + order = PurchaseOrder( + order_no=order_no, + supplier_id=data.supplier_id, + status="draft", + total_amount=0, + remark=data.remark, + created_at=now, + updated_at=now, + ) + session.add(order) + await session.flush() + + items = [] + total_amount = 0 + for item_data in data.items: + amount = item_data.quantity * item_data.price + total_amount += amount + item = PurchaseOrderItem( + product_id=item_data.product_id, + quantity=item_data.quantity, + price=item_data.price, + amount=amount, + received_quantity=0, + remark=item_data.remark, + order_id=order.id, + ) + session.add(item) + items.append(item) + + order.total_amount = total_amount + session.add(order) + await session.commit() + return PurchaseService._order_to_dict(order, items) + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> list: + result = await session.execute( + select(PurchaseOrder) + .offset(offset) + .limit(limit) + ) + orders = list(result.scalars().all()) + result_list = [] + for order in orders: + items_result = await session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + result_list.append(PurchaseService._order_to_dict(order, items)) + return result_list + + @staticmethod + async def get_by_id(order_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(PurchaseOrder).where(PurchaseOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if order: + items_result = await session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + return PurchaseService._order_to_dict(order, items) + return None + + @staticmethod + async def update_status( + order_id, update: PurchaseOrderUpdate, session: AsyncSession + ) -> Optional[dict]: + result = await session.execute( + select(PurchaseOrder).where(PurchaseOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if not order: + return None + update_data = update.model_dump(exclude_unset=True) + update_data["updated_at"] = datetime.now().isoformat() + for key, value in update_data.items(): + setattr(order, key, value) + session.add(order) + await session.commit() + items_result = await session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + return PurchaseService._order_to_dict(order, items) + + @staticmethod + async def delete(order_id, session: AsyncSession) -> bool: + result = await session.execute( + select(PurchaseOrder).where(PurchaseOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if not order: + return False + items_result = await session.execute( + select(PurchaseOrderItem).where(PurchaseOrderItem.order_id == order_id) + ) + for item in items_result.scalars().all(): + await session.delete(item) + await session.delete(order) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/sales_outbound_service.py b/src/chongming/app/service/erp/sales_outbound_service.py new file mode 100644 index 0000000..2c699ef --- /dev/null +++ b/src/chongming/app/service/erp/sales_outbound_service.py @@ -0,0 +1,201 @@ +from typing import Sequence, Optional +from datetime import datetime +from uuid import UUID + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select, and_ + +from ...model.erp.sales_outbound import ( + SalesOutbound, SalesOutboundCreate, SalesOutboundUpdate, + SalesOutboundItem, SalesOutboundItemCreate, +) +from ...model.erp.sales import SalesOrderItem +from ...model.erp.inventory import Inventory +from ...core.order_no_generator import generate_order_no_with_retry +from .inventory_service import InventoryService + + +def _to_uuid(val): + from uuid import UUID + return UUID(val) if isinstance(val, str) else val + + +class SalesOutboundService: + """销售出库单服务类""" + + @staticmethod + def _to_dict(outbound, items=None): + if not outbound: + return None + return { + "id": str(outbound.id), + "outbound_no": outbound.outbound_no, + "sales_order_id": str(outbound.sales_order_id), + "warehouse_id": str(outbound.warehouse_id), + "status": outbound.status, + "total_quantity": outbound.total_quantity, + "remark": outbound.remark, + "operator": outbound.operator, + "created_at": outbound.created_at, + "updated_at": outbound.updated_at, + "items": [ + { + "id": str(item.id), + "outbound_id": str(item.outbound_id), + "product_id": str(item.product_id), + "quantity": item.quantity, + "remark": item.remark, + } + for item in (items or []) + ] if items else [], + } + + @staticmethod + async def create( + data: SalesOutboundCreate, operator: Optional[str], session: AsyncSession + ) -> dict: + now = datetime.now().isoformat() + + # 自动生成出库单号(系统自动生成,不允许手动输入) + outbound_no = await generate_order_no_with_retry( + order_type="sales_outbound", + session=session, + ) + + outbound = SalesOutbound( + outbound_no=outbound_no, + sales_order_id=data.sales_order_id, + warehouse_id=data.warehouse_id, + status="draft", + total_quantity=0, + remark=data.remark, + operator=operator, + created_at=now, + updated_at=now, + ) + session.add(outbound) + await session.flush() + + items = [] + total_qty = 0 + for item_data in data.items: + total_qty += item_data.quantity + item = SalesOutboundItem( + product_id=item_data.product_id, + quantity=item_data.quantity, + remark=item_data.remark, + outbound_id=outbound.id, + ) + session.add(item) + items.append(item) + + outbound.total_quantity = total_qty + session.add(outbound) + await session.commit() + return SalesOutboundService._to_dict(outbound, items) + + @staticmethod + async def approve(outbound_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(SalesOutbound).where(SalesOutbound.id == _to_uuid(outbound_id)) + ) + outbound = result.scalar_one_or_none() + if not outbound or outbound.status != "draft": + return None + + # 检查库存是否充足 + items_result = await session.execute( + select(SalesOutboundItem).where( + SalesOutboundItem.outbound_id == _to_uuid(outbound_id) + ) + ) + items = list(items_result.scalars().all()) + for item in items: + inv = await InventoryService.get_inventory_by_product_and_warehouse( + item.product_id, outbound.warehouse_id, session + ) + if not inv or inv.quantity < item.quantity: + raise ValueError(f"产品 {item.product_id} 库存不足") + + outbound.status = "completed" + outbound.updated_at = datetime.now().isoformat() + session.add(outbound) + await session.commit() + + # 扣减库存 + for item in items: + await InventoryService.adjust_inventory( + product_id=item.product_id, + warehouse_id=outbound.warehouse_id, + quantity=-item.quantity, + order_type="sales_outbound", + order_no=outbound.outbound_no, + change_type="sales_outbound", + remark="销售出库", + operator=outbound.operator, + session=session, + ) + + # 更新销售订单的发货数量 + so_items = await session.execute( + select(SalesOrderItem).where( + SalesOrderItem.order_id == outbound.sales_order_id, + SalesOrderItem.product_id == item.product_id, + ) + ) + for so_item in so_items.scalars().all(): + so_item.delivered_quantity = (so_item.delivered_quantity or 0) + item.quantity + session.add(so_item) + + await session.commit() + return SalesOutboundService._to_dict(outbound, items) + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> list: + result = await session.execute( + select(SalesOutbound).offset(offset).limit(limit) + ) + outbounds = list(result.scalars().all()) + result_list = [] + for outbound in outbounds: + items_result = await session.execute( + select(SalesOutboundItem).where(SalesOutboundItem.outbound_id == outbound.id) + ) + items = list(items_result.scalars().all()) + result_list.append(SalesOutboundService._to_dict(outbound, items)) + return result_list + + @staticmethod + async def get_by_id(outbound_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(SalesOutbound).where(SalesOutbound.id == _to_uuid(outbound_id)) + ) + outbound = result.scalar_one_or_none() + if outbound: + items_result = await session.execute( + select(SalesOutboundItem).where(SalesOutboundItem.outbound_id == outbound.id) + ) + items = list(items_result.scalars().all()) + return SalesOutboundService._to_dict(outbound, items) + return None + + @staticmethod + async def delete(outbound_id, session: AsyncSession) -> bool: + result = await session.execute( + select(SalesOutbound).where(SalesOutbound.id == _to_uuid(outbound_id)) + ) + outbound = result.scalar_one_or_none() + if not outbound: + return False + if outbound.status == "completed": + return False + items_result = await session.execute( + select(SalesOutboundItem).where( + SalesOutboundItem.outbound_id == _to_uuid(outbound_id) + ) + ) + for item in items_result.scalars().all(): + await session.delete(item) + await session.delete(outbound) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/sales_service.py b/src/chongming/app/service/erp/sales_service.py new file mode 100644 index 0000000..7622187 --- /dev/null +++ b/src/chongming/app/service/erp/sales_service.py @@ -0,0 +1,163 @@ +from typing import Sequence, Optional +from datetime import datetime +from uuid import UUID + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.sales import ( + SalesOrder, SalesOrderCreate, SalesOrderUpdate, + SalesOrderItem, SalesOrderItemCreate, +) +from ...core.order_no_generator import generate_order_no_with_retry + + +def _to_uuid(val): + from uuid import UUID + return UUID(val) if isinstance(val, str) else val + + +class SalesService: + """销售订单服务类""" + + @staticmethod + def _order_to_dict(order, items=None): + if not order: + return None + return { + "id": str(order.id), + "order_no": order.order_no, + "customer_id": str(order.customer_id), + "status": order.status, + "total_amount": order.total_amount, + "remark": order.remark, + "created_by": order.created_by, + "approved_by": order.approved_by, + "created_at": order.created_at, + "updated_at": order.updated_at, + "items": [ + { + "id": str(item.id), + "order_id": str(item.order_id), + "product_id": str(item.product_id), + "quantity": item.quantity, + "price": item.price, + "amount": item.amount, + "delivered_quantity": item.delivered_quantity, + "remark": item.remark, + } + for item in (items or []) + ] if items else [], + } + + @staticmethod + async def create(data: SalesOrderCreate, session: AsyncSession) -> dict: + now = datetime.now().isoformat() + + # 自动生成订单号(系统自动生成,不允许手动输入) + order_no = await generate_order_no_with_retry( + order_type="sales_order", + session=session, + ) + + order = SalesOrder( + order_no=order_no, + customer_id=data.customer_id, + status="draft", + total_amount=0, + remark=data.remark, + created_at=now, + updated_at=now, + ) + session.add(order) + await session.flush() + + items = [] + total_amount = 0 + for item_data in data.items: + amount = item_data.quantity * item_data.price + total_amount += amount + item = SalesOrderItem( + product_id=item_data.product_id, + quantity=item_data.quantity, + price=item_data.price, + amount=amount, + delivered_quantity=0, + remark=item_data.remark, + order_id=order.id, + ) + session.add(item) + items.append(item) + + order.total_amount = total_amount + session.add(order) + await session.commit() + return SalesService._order_to_dict(order, items) + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> list: + result = await session.execute( + select(SalesOrder).offset(offset).limit(limit) + ) + orders = list(result.scalars().all()) + result_list = [] + for order in orders: + items_result = await session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + result_list.append(SalesService._order_to_dict(order, items)) + return result_list + + @staticmethod + async def get_by_id(order_id, session: AsyncSession) -> Optional[dict]: + result = await session.execute( + select(SalesOrder).where(SalesOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if order: + items_result = await session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + return SalesService._order_to_dict(order, items) + return None + + @staticmethod + async def update_status( + order_id, update: SalesOrderUpdate, session: AsyncSession + ) -> Optional[dict]: + result = await session.execute( + select(SalesOrder).where(SalesOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if not order: + return None + update_data = update.model_dump(exclude_unset=True) + update_data["updated_at"] = datetime.now().isoformat() + for key, value in update_data.items(): + setattr(order, key, value) + session.add(order) + await session.commit() + items_result = await session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order.id) + ) + items = list(items_result.scalars().all()) + return SalesService._order_to_dict(order, items) + + @staticmethod + async def delete(order_id, session: AsyncSession) -> bool: + result = await session.execute( + select(SalesOrder).where(SalesOrder.id == _to_uuid(order_id)) + ) + order = result.scalar_one_or_none() + if not order: + return False + items_result = await session.execute( + select(SalesOrderItem).where(SalesOrderItem.order_id == order_id) + ) + for item in items_result.scalars().all(): + await session.delete(item) + await session.delete(order) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/supplier_service.py b/src/chongming/app/service/erp/supplier_service.py new file mode 100644 index 0000000..eb9a636 --- /dev/null +++ b/src/chongming/app/service/erp/supplier_service.py @@ -0,0 +1,64 @@ +from typing import Sequence, Optional + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.supplier import Supplier, SupplierCreate, SupplierUpdate +from ...core.order_no_generator import generate_order_no_with_retry + + +class SupplierService: + """供应商服务类""" + + @staticmethod + async def create(data: SupplierCreate, session: AsyncSession) -> Supplier: + # 自动生成供应商编码(系统自动生成,不允许手动输入) + code = await generate_order_no_with_retry( + order_type="supplier", + session=session, + ) + db_obj = Supplier( + code=code, + name=data.name, + contact_person=data.contact_person, + phone=data.phone, + email=data.email, + address=data.address, + is_active=data.is_active, + remark=data.remark, + ) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> Sequence[Supplier]: + result = await session.execute(select(Supplier).offset(offset).limit(limit)) + return result.scalars().all() + + @staticmethod + async def get_by_id(obj_id, session: AsyncSession) -> Optional[Supplier]: + return await session.get(Supplier, obj_id) + + @staticmethod + async def update(obj_id, update: SupplierUpdate, session: AsyncSession) -> Optional[Supplier]: + db_obj = await session.get(Supplier, obj_id) + if not db_obj: + return None + update_data = update.model_dump(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_obj, key, value) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def delete(obj_id, session: AsyncSession) -> bool: + db_obj = await session.get(Supplier, obj_id) + if not db_obj: + return False + await session.delete(db_obj) + await session.commit() + return True diff --git a/src/chongming/app/service/erp/warehouse_service.py b/src/chongming/app/service/erp/warehouse_service.py new file mode 100644 index 0000000..fc4691d --- /dev/null +++ b/src/chongming/app/service/erp/warehouse_service.py @@ -0,0 +1,62 @@ +from typing import Sequence, Optional + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlmodel import select + +from ...model.erp.warehouse import Warehouse, WarehouseCreate, WarehouseUpdate +from ...core.order_no_generator import generate_order_no_with_retry + + +class WarehouseService: + """仓库服务类""" + + @staticmethod + async def create(data: WarehouseCreate, session: AsyncSession) -> Warehouse: + # 自动生成仓库编码(系统自动生成,不允许手动输入) + code = await generate_order_no_with_retry( + order_type="warehouse", + session=session, + ) + db_obj = Warehouse( + code=code, + name=data.name, + address=data.address, + manager=data.manager, + is_active=data.is_active, + description=data.description, + ) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def get_all(offset: int, limit: int, session: AsyncSession) -> Sequence[Warehouse]: + result = await session.execute(select(Warehouse).offset(offset).limit(limit)) + return result.scalars().all() + + @staticmethod + async def get_by_id(obj_id, session: AsyncSession) -> Optional[Warehouse]: + return await session.get(Warehouse, obj_id) + + @staticmethod + async def update(obj_id, update: WarehouseUpdate, session: AsyncSession) -> Optional[Warehouse]: + db_obj = await session.get(Warehouse, obj_id) + if not db_obj: + return None + update_data = update.model_dump(exclude_unset=True) + for key, value in update_data.items(): + setattr(db_obj, key, value) + session.add(db_obj) + await session.commit() + await session.refresh(db_obj) + return db_obj + + @staticmethod + async def delete(obj_id, session: AsyncSession) -> bool: + db_obj = await session.get(Warehouse, obj_id) + if not db_obj: + return False + await session.delete(db_obj) + await session.commit() + return True diff --git a/src/chongming/app/service/system/__init__.py b/src/chongming/app/service/system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/chongming/app/service/auth.py b/src/chongming/app/service/system/auth.py similarity index 99% rename from src/chongming/app/service/auth.py rename to src/chongming/app/service/system/auth.py index f2c8755..64f8df9 100644 --- a/src/chongming/app/service/auth.py +++ b/src/chongming/app/service/system/auth.py @@ -8,8 +8,8 @@ from fastapi import Request import bcrypt from sqlalchemy.ext.asyncio import AsyncSession -from ..model.user import User -from ..core.config import get_config +from ...model import User +from ...core.config import get_config from .user import UserService from plugins.jwt.jwt_cache import get_jwt_cache, TokenResponse, TokenData diff --git a/src/chongming/app/service/permission.py b/src/chongming/app/service/system/permission.py similarity index 88% rename from src/chongming/app/service/permission.py rename to src/chongming/app/service/system/permission.py index 8c75343..2f74b72 100644 --- a/src/chongming/app/service/permission.py +++ b/src/chongming/app/service/system/permission.py @@ -5,12 +5,11 @@ from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select -from ..model import Permission, PermissionUpdate, RolePermission, Role, User -from ..core.cache import acached, get_cache +from ...model import Permission, PermissionUpdate, RolePermission, Role, User +from ...core.cache import acached, get_cache from .user import UserService from .role import RoleService - # 用户权限缓存的 TTL(秒),权限变更后最多 2 分钟自动刷新 _USER_PERMS_CACHE_TTL = 120 @@ -145,9 +144,7 @@ class PermissionService: """ # 检查权限是否被角色使用 result = await session.execute( - select(RolePermission).where( - RolePermission.permission_id == permission_id - ) + select(RolePermission).where(RolePermission.permission_id == permission_id) ) if result.scalars().all(): raise HTTPException( @@ -178,11 +175,11 @@ class PermissionService: :return: 权限对象列表 :raises HTTPException: 当角色不存在时抛出404错误 """ - from ..model.relationship_table import RolePermission as RolePermTB + from ...model import RolePermission as RolePermTB stmt = ( select(Permission) - .join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore + .join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore .where(RolePermTB.role_id == role_id) ) result = await session.execute(stmt) @@ -217,45 +214,54 @@ class PermissionService: # ---- 核心业务方法 ---- @classmethod - @acached(ttl=_USER_PERMS_CACHE_TTL, key_func=lambda cls, session, user_id: f"user_perms:{user_id}") + @acached( + ttl=_USER_PERMS_CACHE_TTL, + key_func=lambda cls, session, user_id: f"user_perms:{user_id}", + ) async def get_permissions_by_user( cls, session: AsyncSession, user_id: UUID, - ) -> List[Permission]: + ) -> List[str]: """ - 获取指定用户的所有权限(通过用户的所有角色汇总) + 获取指定用户的所有权限名称列表(通过用户的所有角色汇总) 使用 @acached 装饰器自动缓存查询结果,缓存键为 user_perms:{user_id}。 TTL 120 秒作为兜底机制。当用户权限发生变更时,主动调用 _invalidate_user_perms_cache() 清除缓存。 + 注意:返回的是权限名称列表(字符串),而不是 Permission 对象列表。 + 这样可以避免 SQLModel 对象在序列化时出现问题。 + :param session: 数据库会话对象 :param user_id: 用户UUID - :return: 去重后的权限对象列表 + :return: 去重后的权限名称列表 :raises HTTPException: 当用户不存在时抛出404错误 """ # 通过 role_permission 和 user_role 关联表进行 JOIN 查询 - from ..model.relationship_table import UserRole as UserRoleTB, RolePermission as RolePermTB + from ...model import ( + UserRole as UserRoleTB, + RolePermission as RolePermTB, + ) stmt = ( - select(Permission) - .join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore - .join(Role, Role.id == RolePermTB.role_id) # type: ignore - .join(UserRoleTB, UserRoleTB.role_id == Role.id) # type: ignore + select(Permission.name) + .join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore + .join(Role, Role.id == RolePermTB.role_id) # type: ignore + .join(UserRoleTB, UserRoleTB.role_id == Role.id) # type: ignore .where(UserRoleTB.user_id == user_id) .distinct() ) result = await session.execute(stmt) - permissions = result.scalars().all() + permission_names = result.scalars().all() - if not permissions: + if not permission_names: # 确认用户是否存在 user = await UserService.get_user_by_id(user_id, session) if not user: raise HTTPException(status_code=404, detail="User not found") - return list(permissions) + return list(permission_names) @classmethod async def assign_permission_to_role( @@ -286,7 +292,9 @@ class PermissionService: ) ) if existing.scalar_one_or_none(): - raise HTTPException(status_code=400, detail="Permission already assigned to role") + raise HTTPException( + status_code=400, detail="Permission already assigned to role" + ) role_permission = RolePermission(role_id=role_id, permission_id=permission_id) session.add(role_permission) @@ -365,12 +373,12 @@ class PermissionService: raise HTTPException(status_code=404, detail="Role not found") # 通过关联表删除记录 - from ..model.relationship_table import RolePermission as RolePermTB + from ...model import RolePermission as RolePermTB from sqlmodel import delete stmt = delete(RolePermTB).where( - RolePermTB.role_id == role_id, # type: ignore - RolePermTB.permission_id == permission_id, # type: ignore + RolePermTB.role_id == role_id, # type: ignore + RolePermTB.permission_id == permission_id, # type: ignore ) await session.execute(stmt) await session.commit() @@ -403,12 +411,12 @@ class PermissionService: if role.id is not None: # 直接通过关联表删除记录 - from ..model.relationship_table import RolePermission as RolePermTB + from ...model import RolePermission as RolePermTB from sqlmodel import delete stmt = delete(RolePermTB).where( - RolePermTB.role_id == role.id, # type: ignore - RolePermTB.permission_id == permission_id, # type: ignore + RolePermTB.role_id == role.id, # type: ignore + RolePermTB.permission_id == permission_id, # type: ignore ) await session.execute(stmt) await session.commit() diff --git a/src/chongming/app/service/role.py b/src/chongming/app/service/system/role.py similarity index 98% rename from src/chongming/app/service/role.py rename to src/chongming/app/service/system/role.py index 71594e1..6d34818 100644 --- a/src/chongming/app/service/role.py +++ b/src/chongming/app/service/system/role.py @@ -5,8 +5,8 @@ from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select -from ..model import Role, RoleUpdate, UserRole -from ..service.user import UserService +from ...model import Role, RoleUpdate, UserRole +from .user import UserService class RoleService: diff --git a/src/chongming/app/service/uploads.py b/src/chongming/app/service/system/uploads.py similarity index 98% rename from src/chongming/app/service/uploads.py rename to src/chongming/app/service/system/uploads.py index bb8236b..e2330ef 100644 --- a/src/chongming/app/service/uploads.py +++ b/src/chongming/app/service/system/uploads.py @@ -6,8 +6,7 @@ from fastapi.exceptions import HTTPException from fastapi import UploadFile import aiofiles -from ..core.config import get_config - +from ...core.config import get_config config = get_config() diff --git a/src/chongming/app/service/system/user.py b/src/chongming/app/service/system/user.py new file mode 100644 index 0000000..2b8f0e3 --- /dev/null +++ b/src/chongming/app/service/system/user.py @@ -0,0 +1,176 @@ +from typing import Optional +from uuid import UUID +import time + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from ...model import User +from ...core.security import get_password_hash + + +class UserService: + """用户服务类,提供用户相关的业务逻辑操作""" + + @staticmethod + async def get_user_by_email(email: str, session: AsyncSession) -> Optional[User]: + """ + 根据邮箱地址查询用户 + + Args: + email: 用户的邮箱地址 + session: 异步数据库会话 + + Returns: + 如果找到则返回用户对象,否则返回None + """ + result = await session.execute(select(User).where(User.email == email)) # type: ignore + return result.scalar_one_or_none() + + @staticmethod + async def get_user_by_id(user_id: UUID, session: AsyncSession) -> Optional[User]: + """ + 根据用户ID查询用户 + + Args: + user_id: 用户的UUID标识 + session: 异步数据库会话 + + Returns: + 如果找到则返回用户对象,否则返回None + """ + return await session.get(User, user_id) + + @staticmethod + async def create_user( + session: AsyncSession, email: str, password: str, **kwargs + ) -> User: + """ + 创建新用户 + + Args: + session: 异步数据库会话 + email: 用户的邮箱地址 + password: 用户的明文密码,将自动进行哈希加密 + **kwargs: 其他用户属性参数 + + Returns: + 创建成功的用户对象 + + Raises: + IntegrityError: 当邮箱已存在时可能抛出完整性错误 + """ + hashed = get_password_hash(password) + user = User( + email=email, + hashed_password=hashed, + created_at=str(time.time() * 1000), + **kwargs + ) + session.add(user) + await session.commit() + await session.refresh(user) + return user + + @staticmethod + async def list_users( + session: AsyncSession, + skip: int = 0, + limit: int = 100, + ) -> list[User]: + """ + 获取用户列表,支持分页 + + Args: + session: 异步数据库会话 + skip: 跳过的记录数 + limit: 返回的最大记录数 + + Returns: + 用户对象列表 + """ + from sqlalchemy import select + from sqlalchemy.orm import selectinload + + result = await session.execute( + select(User).options(selectinload(User.roles)).offset(skip).limit(limit) # type: ignore + ) + return list(result.scalars().all()) + + @staticmethod + async def update_user( + session: AsyncSession, + user_id: UUID, + update_data: dict, + ) -> Optional[User]: + """ + 更新用户信息 + + Args: + session: 异步数据库会话 + user_id: 用户UUID + update_data: 要更新的字段字典 + + Returns: + 更新后的用户对象,用户不存在返回None + """ + user = await UserService.get_user_by_id(user_id, session) + if not user: + return None + + # 如果包含密码字段,进行哈希处理 + if "password" in update_data and update_data["password"]: + from ...core.security import get_password_hash + + update_data["hashed_password"] = get_password_hash( + update_data.pop("password") + ) + + for key, value in update_data.items(): + if value is not None and hasattr(user, key): + setattr(user, key, value) + + await session.commit() + await session.refresh(user) + return user + + @staticmethod + async def delete_user( + session: AsyncSession, + user_id: UUID, + ) -> bool: + """ + 删除用户 + + Args: + session: 异步数据库会话 + user_id: 用户UUID + + Returns: + 删除成功返回True,用户不存在返回False + """ + user = await UserService.get_user_by_id(user_id, session) + if not user: + return False + + await session.delete(user) + await session.commit() + return True + + @staticmethod + async def count_users( + session: AsyncSession, + ) -> int: + """ + 获取用户总数 + + Args: + session: 异步数据库会话 + + Returns: + 用户总数 + """ + from sqlalchemy import select, func + + result = await session.execute(select(func.count(User.id))) # type: ignore + return result.scalar() or 0 diff --git a/src/chongming/app/service/todo.py b/src/chongming/app/service/todo.py deleted file mode 100644 index d88fc38..0000000 --- a/src/chongming/app/service/todo.py +++ /dev/null @@ -1,121 +0,0 @@ -from typing import Sequence, Optional - -from sqlalchemy.ext.asyncio import AsyncSession -from sqlmodel import select - -from ..model.todo import TodoCreate, Todo, TodoUpdate - - -class TodoService: - """待办事项服务类,提供待办事项的增删改查操作""" - - @staticmethod - async def create_todo(todo: TodoCreate, session: AsyncSession) -> Todo: - """ - 创建新的待办事项 - - Args: - todo: 待办事项创建数据对象,包含待办事项的初始信息 - session: 异步数据库会话对象,用于执行数据库操作 - - Returns: - Todo: 创建成功后的待办事项对象,包含生成的ID等信息 - """ - db_todo = Todo.model_validate(todo) - session.add(db_todo) - await session.commit() - await session.refresh(db_todo) - return db_todo - - @staticmethod - async def get_todos( - offset: int, - limit: int, - session: AsyncSession, - ) -> Sequence[Todo]: - """ - 获取待办事项列表(支持分页) - - Args: - offset: 查询偏移量,指定从第几条记录开始查询 - limit: 查询限制数量,指定最多返回多少条记录 - session: 异步数据库会话对象,用于执行数据库操作 - - Returns: - Sequence[Todo]: 待办事项对象列表,按查询条件返回的结果集 - """ - result = await session.execute(select(Todo).offset(offset).limit(limit)) - todos = result.scalars().all() - return todos - - @staticmethod - async def get_todo_by_id( - todo_id: int, - session: AsyncSession, - ) -> Optional[Todo]: - """ - 根据ID获取单个待办事项 - - Args: - todo_id: 待办事项的唯一标识ID - session: 异步数据库会话对象,用于执行数据库操作 - - Returns: - Optional[Todo]: 如果找到则返回待办事项对象,否则返回None - """ - todo = await session.get(Todo, todo_id) - return todo - - @staticmethod - async def update_todo( - todo_id: int, - todo_update: TodoUpdate, - session: AsyncSession, - ) -> Optional[Todo]: - """ - 更新待办事项信息 - - Args: - todo_id: 待办事项的唯一标识ID - todo_update: 待办事项更新数据对象,包含需要更新的字段信息 - session: 异步数据库会话对象,用于执行数据库操作 - - Returns: - Optional[Todo]: 如果更新成功则返回更新后的待办事项对象,如果待办事项不存在则返回None - """ - todo = await session.get(Todo, todo_id) - if not todo: - return None - - # 提取需要更新的字段数据,排除未设置的字段 - update_data = todo_update.model_dump(exclude_unset=True) - - for key, value in update_data.items(): - setattr(todo, key, value) - - session.add(todo) - await session.commit() - await session.refresh(todo) - return todo - - @staticmethod - async def delete_todo( - todo_id: int, - session: AsyncSession, - ) -> bool: - """ - 删除待办事项 - - Args: - todo_id: 待办事项的唯一标识ID - session: 异步数据库会话对象,用于执行数据库操作 - - Returns: - bool: 如果删除成功返回True,如果待办事项不存在返回False - """ - todo = await session.get(Todo, todo_id) - if not todo: - return False - await session.delete(todo) - await session.commit() - return True diff --git a/src/chongming/app/service/user.py b/src/chongming/app/service/user.py deleted file mode 100644 index 302b6d1..0000000 --- a/src/chongming/app/service/user.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import Optional -from uuid import UUID -import time - -from sqlalchemy import select -from sqlalchemy.ext.asyncio import AsyncSession - -from ..model.user import User -from ..core.security import get_password_hash - - -class UserService: - """用户服务类,提供用户相关的业务逻辑操作""" - - @staticmethod - async def get_user_by_email(email: str, session: AsyncSession) -> Optional[User]: - """ - 根据邮箱地址查询用户 - - Args: - email: 用户的邮箱地址 - session: 异步数据库会话 - - Returns: - 如果找到则返回用户对象,否则返回None - """ - result = await session.execute(select(User).where(User.email == email)) # type: ignore - return result.scalar_one_or_none() - - @staticmethod - async def get_user_by_id(user_id: UUID, session: AsyncSession) -> Optional[User]: - """ - 根据用户ID查询用户 - - Args: - user_id: 用户的UUID标识 - session: 异步数据库会话 - - Returns: - 如果找到则返回用户对象,否则返回None - """ - return await session.get(User, user_id) - - @staticmethod - async def create_user( - session: AsyncSession, email: str, password: str, **kwargs - ) -> User: - """ - 创建新用户 - - Args: - session: 异步数据库会话 - email: 用户的邮箱地址 - password: 用户的明文密码,将自动进行哈希加密 - **kwargs: 其他用户属性参数 - - Returns: - 创建成功的用户对象 - - Raises: - IntegrityError: 当邮箱已存在时可能抛出完整性错误 - """ - hashed = get_password_hash(password) - user = User(email=email, hashed_password=hashed, created_at=str(time.time() * 1000), **kwargs) - session.add(user) - await session.commit() - await session.refresh(user) - return user diff --git a/src/chongming/app/task/dev_init_db.py b/src/chongming/app/task/dev_init_db.py index e6fcfa8..b0f8fe7 100644 --- a/src/chongming/app/task/dev_init_db.py +++ b/src/chongming/app/task/dev_init_db.py @@ -3,14 +3,14 @@ from sqlmodel import select from ..core.logger import get_logger from ..constant.permission import PermissionConstant -from ..service.user import UserService -from ..service.role import RoleService -from ..service.permission import PermissionService -from ..model.role import Role -from ..model.user import User +from ..service.system.user import UserService +from ..service.system.role import RoleService +from ..service.system.permission import PermissionService +from ..model import Role +from ..model import User from plugins.scheduler.scheduler import get_task_service_instance - +from ..service.erp.code_rule_service import CodeRuleService logger = get_logger("scheduler") @@ -19,10 +19,10 @@ logger = get_logger("scheduler") # ────────────────────────────────────────────── # 角色名称常量 -ROLE_ADMIN = "admin" # 超级管理员 - 拥有所有权限 -ROLE_MANAGER = "manager" # 经理 - 拥有业务模块完整操作权限 +ROLE_ADMIN = "admin" # 超级管理员 - 拥有所有权限 +ROLE_MANAGER = "manager" # 经理 - 拥有业务模块完整操作权限 ROLE_OPERATOR = "operator" # 操作员 - 拥有业务模块基本操作权限 -ROLE_VIEWER = "viewer" # 访客 - 仅有读取权限 +ROLE_VIEWER = "viewer" # 访客 - 仅有读取权限 async def init_permission(session): @@ -65,6 +65,7 @@ async def init_permission(session): ) logger.info(f"创建权限: {permission} - {description}") + async def dev_init_admin(session): """ 初始化系统管理员用户 @@ -93,7 +94,9 @@ async def dev_init_admin(session): if admin_user: logger.info("管理员用户已存在") return - user = await UserService.create_user(session, "admin@qq.com", "admin", is_superuser=True) + user = await UserService.create_user( + session, "admin@qq.com", "admin", is_superuser=True + ) admin_role = await RoleService.get_role_by_name(session, ROLE_ADMIN) if admin_role is None or admin_role.id is None: raise ValueError("管理员未创建") @@ -122,12 +125,12 @@ def _get_manager_permissions() -> list: """ # 系统管理权限 - 经理不包含 exclude_prefixes = { - PermissionConstant.PREFIX_PERMISSION, - PermissionConstant.PREFIX_ROLE, - PermissionConstant.PREFIX_PERMISSION, - PermissionConstant.PREFIX_SYSTEM_CONFIG, - PermissionConstant.PREFIX_AUDIT_LOG, - PermissionConstant.PREFIX_BACKUP, + PermissionConstant.SYSTEM.PREFIX_PERMISSION, + PermissionConstant.SYSTEM.PREFIX_ROLE, + PermissionConstant.SYSTEM.PREFIX_PERMISSION, + PermissionConstant.SYSTEM.PREFIX_SYSTEM_CONFIG, + PermissionConstant.SYSTEM.PREFIX_AUDIT_LOG, + PermissionConstant.SYSTEM.PREFIX_BACKUP, } result = [] @@ -157,12 +160,12 @@ def _get_operator_permissions() -> list: exclude_actions = {"delete", "approve", "cancel", "import", "export"} # 操作员排除的资源前缀 exclude_prefixes = { - PermissionConstant.PREFIX_PERSONNEL, - PermissionConstant.PREFIX_ROLE, - PermissionConstant.PREFIX_PERMISSION, - PermissionConstant.PREFIX_SYSTEM_CONFIG, - PermissionConstant.PREFIX_AUDIT_LOG, - PermissionConstant.PREFIX_BACKUP, + PermissionConstant.SYSTEM.PREFIX_PERSONNEL, + PermissionConstant.SYSTEM.PREFIX_ROLE, + PermissionConstant.SYSTEM.PREFIX_PERMISSION, + PermissionConstant.SYSTEM.PREFIX_SYSTEM_CONFIG, + PermissionConstant.SYSTEM.PREFIX_AUDIT_LOG, + PermissionConstant.SYSTEM.PREFIX_BACKUP, } result = [] @@ -193,13 +196,22 @@ def _get_viewer_permissions() -> list: 获取访客角色的权限列表。 访客只有最基本的读取和查看权限,没有任何写入、修改或删除权限。 + 包含 ERP 业务模块的只读查看权限(客户、产品、供应商、仓库、库存、采购、销售等)。 Returns: list[str]: 权限名称列表 """ read_actions = {"read", "view"} read_prefixes = { - PermissionConstant.PREFIX_TODO + PermissionConstant.ERP.PREFIX_CUSTOMER, + PermissionConstant.ERP.PREFIX_PRODUCT, + PermissionConstant.ERP.PREFIX_SUPPLIER, + PermissionConstant.ERP.PREFIX_WAREHOUSE, + PermissionConstant.ERP.PREFIX_INVENTORY, + PermissionConstant.ERP.PREFIX_PURCHASE, + PermissionConstant.ERP.PREFIX_PURCHASE_INBOUND, + PermissionConstant.ERP.PREFIX_SALES, + PermissionConstant.ERP.PREFIX_SALES_OUTBOUND, } result = [] @@ -273,9 +285,9 @@ async def init_default_roles(session): expected_permission_names = get_permissions_fn() # 获取角色当前已关联的权限名称集合 - existing_permission_names = { - p.name for p in role.permissions - } if role else set() + existing_permission_names = ( + {p.name for p in role.permissions} if role else set() + ) # 分配缺失的权限 for perm_name in expected_permission_names: @@ -304,13 +316,9 @@ async def init_default_roles(session): admin_role = await RoleService.get_role_by_name(session, ROLE_ADMIN) if admin_user and admin_role and admin_role.id is not None: # 检查是否已关联 - already_assigned = any( - r.id == admin_role.id for r in admin_user.roles - ) + already_assigned = any(r.id == admin_role.id for r in admin_user.roles) if not already_assigned: - await RoleService.assign_role_to_user( - session, admin_user.id, admin_role.id - ) + await RoleService.assign_role_to_user(session, admin_user.id, admin_role.id) logger.info("为 admin 用户分配 admin 角色") @@ -338,5 +346,7 @@ async def init_db(): await init_permission(session) # 再初始化默认角色及权限关联 await init_default_roles(session) - # 最后初始化管理员用户 + # 初始化管理员用户 await dev_init_admin(session) + # 初始化默认编码规则 + await CodeRuleService.init_default_rules(session) diff --git a/src/chongming/scripts/build.py b/src/chongming/scripts/build.py index 0b09244..3de2278 100644 --- a/src/chongming/scripts/build.py +++ b/src/chongming/scripts/build.py @@ -39,17 +39,21 @@ is_windows = platform.system() == "Windows" # ── 构建结果数据结构 ───────────────────────────────────────────────────────── + @dataclass class BuildStepResult: """单个构建步骤的执行结果""" - name: str # 步骤名称 - success: bool # 是否成功 - duration: float # 耗时(秒) + + name: str # 步骤名称 + success: bool # 是否成功 + duration: float # 耗时(秒) message: str = "" # 附加信息(如包大小) + @dataclass class BuildReport: """构建总结报告""" + mode: str start_time: float end_time: float = 0.0 @@ -80,11 +84,15 @@ class BuildReport: for i, step in enumerate(self.steps, 1): icon = "✅" if step.success else "❌" msg = f" ({step.message})" if step.message else "" - print(f" {icon} [{i}/{len(self.steps)}] {step.name:<20s} {step.duration:>7.2f}s{msg}") + print( + f" {icon} [{i}/{len(self.steps)}] {step.name:<20s} {step.duration:>7.2f}s{msg}" + ) print(f"{sep}\n") + # ── 工具函数 ──────────────────────────────────────────────────────────────── + def _run_cmd( cmd: list[str], cwd: str | None = None, @@ -104,6 +112,7 @@ def _run_cmd( errors="replace", ) + def _format_size(path: str | Path) -> str: """获取文件可读大小""" size = Path(path).stat().st_size @@ -113,10 +122,12 @@ def _format_size(path: str | Path) -> str: size /= 1024.0 return f"{size:.2f} TB" + def _ensure_dir(path: str | Path): """确保目录存在""" Path(path).mkdir(parents=True, exist_ok=True) + def _resolve_mode() -> str: """解析构建模式""" mode = PYINSTALLER @@ -127,8 +138,10 @@ def _resolve_mode() -> str: sys.exit(1) return mode + # ── 阶段 1: 模块打包 (module-bank) ────────────────────────────────────────── + def _stage_pack_modules(config: Config) -> str: """ 使用 module-bank 加密打包 Python 模块为 .mbank 文件。 @@ -143,51 +156,19 @@ def _stage_pack_modules(config: Config) -> str: for output_path, source_dir, package_name in mbank_list: key = Encryption.generate_key() packer = PythonToSQLite( - output_path, + output_path, key, - pyarmor_obfuscate=True, + pyarmor_obfuscate=True, ) packer.pack_directory(source_dir, package_name) packer.verify_package_structure() packer.packer_key(key) packer.delete_source_code(None) - - # 修复 metadata 格式: 将 str(dict) 格式转换为 json 格式 - # sqlite_module_importer.add_module() 使用 str(metadata) 存储, - # 生成 "{'pyarmor_runtime': 'pyarmor_runtime_000000'}" 这样的单引号格式, - # 但运行时 loader._extract_runtime_name_from_metadata() 使用 json.loads() 解析. - # 单引号不是合法的 JSON, 导致解析失败, 无法获取运行时模块名. - # 这里在删除源码后修复 metadata 格式, 使得 loader 能从 metadata 中提取运行时名. - # try: - # import sqlite3 as _sqlite3 - # _conn = _sqlite3.connect(output_path) - # _conn.row_factory = _sqlite3.Row - # _rows = _conn.execute( - # "SELECT module_name, metadata FROM python_modules WHERE metadata IS NOT NULL" - # ).fetchall() - # for _row in _rows: - # _meta_str = _row["metadata"] - # try: - # json.loads(_meta_str) # 已经是合法 JSON, 无需处理 - # except (json.JSONDecodeError, TypeError): - # try: - # _meta_dict = ast.literal_eval(_meta_str) - # _fixed = json.dumps(_meta_dict) - # _conn.execute( - # "UPDATE python_modules SET metadata = ? WHERE module_name = ?", - # (_fixed, _row["module_name"]), - # ) - # except (ValueError, SyntaxError): - # pass - # _conn.commit() - # _conn.close() - # except Exception: - # pass # metadata 修复非关键路径, 不应阻塞构建 - size = _format_size(output_path) messages.append(f"{Path(output_path).name} ({size})") return " - " + "\n - ".join(messages) + def _stage_prepare_build_dir(config: Config, mode: str) -> str: """准备 build 目录并复制必要文件""" build_path = Path("build") @@ -218,8 +199,10 @@ def _stage_prepare_build_dir(config: Config, mode: str) -> str: return "资源准备完成" + # ── 阶段 3: PyArmor 混淆 ─────────────────────────────────────────────────── + def _stage_pyarmor_obfuscate() -> str: """对关键脚本执行 PyArmor 混淆""" files = [ @@ -240,28 +223,39 @@ def _stage_pyarmor_obfuscate() -> str: return f"❌ 混淆失败: {', '.join(failed)}" return "✅ 所有文件混淆成功" + # ── 阶段 4: PyInstaller 打包 ──────────────────────────────────────────────── + def _stage_pyinstaller(config: Config) -> tuple[str, str]: """执行 PyInstaller 打包,返回 (状态消息, 可执行文件路径)""" base_imports = config.get("pyinstaller_build_import", {}) build_imports = list(base_imports.get("base", [])) build_imports.extend( - base_imports.get("windows", []) if is_windows - else base_imports.get("linux", []) + base_imports.get("windows", []) if is_windows else base_imports.get("linux", []) ) icon = Path("../../public/chongming.ico") cmd = [ - sys.executable, "-m", "PyInstaller", - "--onefile", "--noconfirm", - "--optimize", "2", "--strip", - "--name", "chongming", - "--distpath", ".", - "--workpath", "./build_temp", - "--specpath", "./specs", + sys.executable, + "-m", + "PyInstaller", + "--onefile", + "--noconfirm", + "--optimize", + "2", + "--name", + "chongming", + "--distpath", + ".", + "--workpath", + "./build_temp", + "--specpath", + "./specs", "--clean", ] + if not is_windows: + cmd.append("--strip") if icon.exists(): cmd.extend(["--icon", str(icon)]) for imp in build_imports: @@ -283,8 +277,10 @@ def _stage_pyinstaller(config: Config) -> tuple[str, str]: return f"✅ PyInstaller 打包成功 ({size})", str(exe) return "✅ PyInstaller 打包完成 (但未找到可执行文件)", "" + # ── 阶段 5: Nuitka 打包 ──────────────────────────────────────────────────── + def _stage_nuitka(config: Config) -> tuple[str, str]: """执行 Nuitka 打包,返回 (状态消息, 可执行文件路径)""" base_imports = config.get("pyinstaller_build_import", {}).get("base", []) @@ -293,14 +289,29 @@ def _stage_nuitka(config: Config) -> tuple[str, str]: ) packages = [ - "module_bank", "sqlite_vfs", "fastapi", "uvicorn", "gunicorn", - "diskcache", "bcrypt", "apscheduler", "aiofiles", "aiosqlite", - "sqlmodel", "jwt", "python_multipart", "tomli", "typing_extensions", + "module_bank", + "sqlite_vfs", + "fastapi", + "uvicorn", + "gunicorn", + "diskcache", + "bcrypt", + "apscheduler", + "aiofiles", + "aiosqlite", + "sqlmodel", + "jwt", + "python_multipart", + "tomli", + "typing_extensions", ] cmd = [ - sys.executable, "-m", "nuitka", - "--standalone", "--onefile", + sys.executable, + "-m", + "nuitka", + "--standalone", + "--onefile", "--follow-imports", "--static-libpython=no", "--remove-output", @@ -308,7 +319,8 @@ def _stage_nuitka(config: Config) -> tuple[str, str]: "--lto=yes", "--disable-plugin=tk-inter", "--disable-plugin=numpy", - "--output-dir=.", "--output-filename=chongming", + "--output-dir=.", + "--output-filename=chongming", "main.py", "--jobs=2", ] @@ -329,8 +341,7 @@ def _stage_nuitka(config: Config) -> tuple[str, str]: except subprocess.CalledProcessError: # 降级重试(移除可能不兼容的参数) fallback = [ - arg for arg in cmd - if arg not in ("--lto=yes", "--enable-plugin=upx") + arg for arg in cmd if arg not in ("--lto=yes", "--enable-plugin=upx") ] _run_cmd(fallback, cwd="build", capture=False) @@ -346,13 +357,17 @@ def _stage_nuitka(config: Config) -> tuple[str, str]: return "❌ Nuitka 编译失败 (未生成可执行文件)", "" + # ── 阶段 6: 清理临时文件 ──────────────────────────────────────────────────── + def _stage_cleanup(mode: str) -> str: """清理构建过程中的临时文件""" build = Path("build") dirs_to_remove = [ - "build_temp", "specs", "utils", + "build_temp", + "specs", + "utils", "pyarmor_runtime_000000", "__pycache__", ] @@ -370,8 +385,10 @@ def _stage_cleanup(mode: str) -> str: return "临时文件已清理" + # ── 阶段 7: 数据库初始化 ──────────────────────────────────────────────────── + async def _stage_init_database(): """初始化数据库表结构和默认数据""" from sqlalchemy.ext.asyncio import create_async_engine @@ -395,7 +412,9 @@ async def _stage_init_database(): "sqlite+aiosqlite:///./build/data/database.db", **db_config, ) - session_maker = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) + session_maker = async_sessionmaker( + engine, class_=AsyncSession, expire_on_commit=False + ) async with engine.begin() as conn: if db_type == "sqlite": @@ -417,8 +436,10 @@ async def _stage_init_database(): db_size = _format_size("build/data/database.db") return f"✅ 数据库初始化完成 ({db_size})" + # ── 阶段 8: Vue 前端构建 ──────────────────────────────────────────────────── + def _stage_build_vue() -> str: """构建 Vue 前端并打包为 SQLite VFS""" dist_dir = "./src/chongming-web/dist" @@ -450,8 +471,10 @@ def _stage_build_vue() -> str: size = _format_size(output_db) return f"✅ Vue 打包完成 ({size})" + # ── 主流程 ────────────────────────────────────────────────────────────────── + async def main(): """规范化构建主流程""" mode = _resolve_mode() @@ -497,8 +520,12 @@ async def main(): msg, exe_path = _stage_pyinstaller(config) elapsed = time.time() - start success = "❌" not in msg - report.add_step("PyInstaller 打包", success, elapsed, msg.replace("✅ PyInstaller ", "")) - print(f" {'✅' if success else '❌'} PyInstaller 打包{' ' * 12} {elapsed:>7.2f}s {msg}") + report.add_step( + "PyInstaller 打包", success, elapsed, msg.replace("✅ PyInstaller ", "") + ) + print( + f" {'✅' if success else '❌'} PyInstaller 打包{' ' * 12} {elapsed:>7.2f}s {msg}" + ) except Exception as e: elapsed = time.time() - start report.add_step("PyInstaller 打包", False, elapsed, str(e)[:200]) diff --git a/src/chongming/scripts/code_content.py b/src/chongming/scripts/code_content.py index 8b2284e..a5e69fc 100644 --- a/src/chongming/scripts/code_content.py +++ b/src/chongming/scripts/code_content.py @@ -11,6 +11,7 @@ def generate_markdown_from_py_files(directory, output_file): dirs[:] = [d for d in dirs if d != "node_modules"] dirs[:] = [d for d in dirs if d != "build"] dirs[:] = [d for d in dirs if d != "dist"] + dirs[:] = [d for d in dirs if d != "chongming-web"] for file in files: if ( file.endswith(".py") diff --git a/src/plugins/jwt/jwt_cache.py b/src/plugins/jwt/jwt_cache.py index f77d9bc..a4cf363 100644 --- a/src/plugins/jwt/jwt_cache.py +++ b/src/plugins/jwt/jwt_cache.py @@ -286,19 +286,42 @@ class JWTCache: with self.lock: cache_data = self.cache.get(f"token:{token_hash}") if not cache_data: - return None + # 缓存中找不到 token 数据时,如果 JWT 本身有效,则重建缓存 + # 这解决了服务重启或缓存丢失后 token 仍可正常使用的问题 + expires_dt = datetime.fromtimestamp(payload.get("exp", 0)) + if datetime.utcnow() > expires_dt: + return None + cache_data = { + "token_hash": token_hash, + "user_id": user_id, + "device_id": payload.get("device_id"), + "user_agent": None, + "ip_address": None, + "created_at": datetime.fromtimestamp(payload.get("iat", 0)).isoformat(), + "expires_at": expires_dt.isoformat(), + "last_used": datetime.utcnow().isoformat(), + "is_active": True, + "payload_data": payload, + "token_type": token_type, + } + self.cache.set(f"token:{token_hash}", cache_data) # 检查 Token 是否激活 if not cache_data.get("is_active", True): return None - # 检查是否过期 - expires_at = datetime.fromisoformat(cache_data["expires_at"]) - if datetime.utcnow() > expires_at: - # 标记为不活跃 - cache_data["is_active"] = False - self.cache.set(f"token:{token_hash}", cache_data) - return None + # 检查是否过期(从缓存数据中读取) + expires_at_str = cache_data.get("expires_at") + if expires_at_str: + try: + expires_at = datetime.fromisoformat(expires_at_str) + if datetime.utcnow() > expires_at: + # 标记为不活跃 + cache_data["is_active"] = False + self.cache.set(f"token:{token_hash}", cache_data) + return None + except ValueError: + pass # 更新最后使用时间 cache_data["last_used"] = datetime.utcnow().isoformat() diff --git a/uv.lock b/uv.lock index 4f543bb..2b4fe55 100644 --- a/uv.lock +++ b/uv.lock @@ -3,7 +3,8 @@ revision = 3 requires-python = ">=3.8" resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", "python_full_version < '3.9'", ] @@ -25,7 +26,8 @@ version = "25.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } wheels = [ @@ -53,7 +55,8 @@ version = "0.22.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } wheels = [ @@ -113,7 +116,8 @@ name = "anyio" version = "4.12.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "exceptiongroup", marker = "python_full_version == '3.9.*'" }, @@ -325,7 +329,8 @@ version = "2.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*' and implementation_name != 'PyPy'" }, @@ -434,7 +439,7 @@ dependencies = [ { name = "fastapi", version = "0.128.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "fastapi", version = "0.136.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "gunicorn", version = "23.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "gunicorn", version = "25.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "gunicorn", version = "26.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "module-bank" }, { name = "pyjwt", version = "2.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "pyjwt", version = "2.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, @@ -495,7 +500,8 @@ name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", "python_full_version < '3.9'", ] dependencies = [ @@ -534,11 +540,15 @@ wheels = [ name = "cryptography" version = "47.0.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.9'", + "python_full_version < '3.9'", +] dependencies = [ { name = "cffi", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9' and platform_python_implementation != 'PyPy'" }, - { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and platform_python_implementation != 'PyPy'" }, + { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9' and platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" } wheels = [ @@ -592,6 +602,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/9c/51f28c3550276bcf35660703ba0ab829a90b88be8cd98a71ef23c2413913/cryptography-47.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8", size = 3698916, upload-time = "2026-04-24T19:54:49.782Z" }, ] +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", +] +dependencies = [ + { name = "cffi", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9' and platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9' and python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, +] + [[package]] name = "diskcache" version = "5.6.3" @@ -637,11 +711,12 @@ name = "fastapi" version = "0.128.8" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "annotated-doc", marker = "python_full_version == '3.9.*'" }, - { name = "pydantic", version = "2.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "starlette", version = "0.49.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "typing-inspection", marker = "python_full_version == '3.9.*'" }, @@ -660,7 +735,7 @@ resolution-markers = [ ] dependencies = [ { name = "annotated-doc", marker = "python_full_version >= '3.10'" }, - { name = "pydantic", version = "2.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "starlette", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "typing-inspection", marker = "python_full_version >= '3.10'" }, @@ -742,7 +817,8 @@ name = "greenlet" version = "3.2.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/b0/f5/3e9eafb4030588337b2a2ae4df46212956854e9069c07b53aa3caabafd47/greenlet-3.2.5.tar.gz", hash = "sha256:c816554eb33e7ecf9ba4defcb1fd8c994e59be6b4110da15480b3e7447ea4286", size = 191501, upload-time = "2026-02-20T20:08:51.539Z" } wheels = [ @@ -850,7 +926,8 @@ name = "gunicorn" version = "23.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", "python_full_version < '3.9'", ] dependencies = [ @@ -863,7 +940,7 @@ wheels = [ [[package]] name = "gunicorn" -version = "25.3.0" +version = "26.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", @@ -871,9 +948,9 @@ resolution-markers = [ dependencies = [ { name = "packaging", marker = "python_full_version >= '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/f4/e78fa054248fab913e2eab0332c6c2cb07421fca1ce56d8fe43b6aef57a4/gunicorn-25.3.0.tar.gz", hash = "sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889", size = 634883, upload-time = "2026-03-27T00:00:26.092Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/b7/a4a3f632f823e432ce6bc65f62961b7980c898c77f075a2f7118cb3846fe/gunicorn-26.0.0.tar.gz", hash = "sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf", size = 727286, upload-time = "2026-05-05T06:38:25.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/c8/8aaf447698c4d59aa853fd318eed300b5c9e44459f242ab8ead6c9c09792/gunicorn-25.3.0-py3-none-any.whl", hash = "sha256:cacea387dab08cd6776501621c295a904fe8e3b7aae9a1a3cbb26f4e7ed54660", size = 208403, upload-time = "2026-03-27T00:00:27.386Z" }, + { url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" }, ] [[package]] @@ -914,7 +991,8 @@ name = "importlib-metadata" version = "8.7.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "zipp", version = "3.23.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, @@ -941,7 +1019,8 @@ name = "module-bank" version = "0.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography" }, + { name = "cryptography", version = "47.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.9'" }, + { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9'" }, { name = "pyarmor" }, ] wheels = [ @@ -1076,7 +1155,8 @@ name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } @@ -1115,21 +1195,22 @@ wheels = [ [[package]] name = "pydantic" -version = "2.13.3" +version = "2.13.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "annotated-types", marker = "python_full_version >= '3.9'" }, - { name = "pydantic-core", version = "2.46.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "pydantic-core", version = "2.46.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, { name = "typing-inspection", marker = "python_full_version >= '3.9'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068, upload-time = "2026-04-20T14:46:43.632Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981, upload-time = "2026-04-20T14:46:41.402Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, ] [[package]] @@ -1247,136 +1328,137 @@ wheels = [ [[package]] name = "pydantic-core" -version = "2.46.3" +version = "2.46.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412, upload-time = "2026-04-20T14:40:56.672Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/98/b50eb9a411e87483b5c65dba4fa430a06bac4234d3403a40e5a9905ebcd0/pydantic_core-2.46.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da3786b8018e60349680720158cc19161cc3b4bdd815beb0a321cd5ce1ad5b1", size = 2108971, upload-time = "2026-04-20T14:43:51.945Z" }, - { url = "https://files.pythonhosted.org/packages/08/4b/f364b9d161718ff2217160a4b5d41ce38de60aed91c3689ebffa1c939d23/pydantic_core-2.46.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc0988cb29d21bf4a9d5cf2ef970b5c0e38d8d8e107a493278c05dc6c1dda69f", size = 1949588, upload-time = "2026-04-20T14:44:10.386Z" }, - { url = "https://files.pythonhosted.org/packages/8f/8b/30bd03ee83b2f5e29f5ba8e647ab3c456bf56f2ec72fdbcc0215484a0854/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f9067c3bfadd04c55484b89c0d267981b2f3512850f6f66e1e74204a4e4ce3", size = 1975986, upload-time = "2026-04-20T14:43:57.106Z" }, - { url = "https://files.pythonhosted.org/packages/3c/54/13ccf954d84ec275d5d023d5786e4aa48840bc9f161f2838dc98e1153518/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a642ac886ecf6402d9882d10c405dcf4b902abeb2972cd5fb4a48c83cd59279a", size = 2055830, upload-time = "2026-04-20T14:44:15.499Z" }, - { url = "https://files.pythonhosted.org/packages/be/0e/65f38125e660fdbd72aa858e7dfae893645cfa0e7b13d333e174a367cd23/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79f561438481f28681584b89e2effb22855e2179880314bcddbf5968e935e807", size = 2222340, upload-time = "2026-04-20T14:41:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/d1/88/f3ab7739efe0e7e80777dbb84c59eb98518e3f57ea433206194c2e425272/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57a973eae4665352a47cf1a99b4ee864620f2fe663a217d7a8da68a1f3a5bfda", size = 2280727, upload-time = "2026-04-20T14:41:30.461Z" }, - { url = "https://files.pythonhosted.org/packages/2a/6d/c228219080817bec4982f9531cadb18da6aaa770fdeb114f49c237ac2c9f/pydantic_core-2.46.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83d002b97072a53ea150d63e0a3adfae5670cef5aa8a6e490240e482d3b22e57", size = 2092158, upload-time = "2026-04-20T14:44:07.305Z" }, - { url = "https://files.pythonhosted.org/packages/0f/b1/525a16711e7c6d61635fac3b0bd54600b5c5d9f60c6fc5aaab26b64a2297/pydantic_core-2.46.3-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b40ddd51e7c44b28cfaef746c9d3c506d658885e0a46f9eeef2ee815cbf8e045", size = 2116626, upload-time = "2026-04-20T14:42:34.118Z" }, - { url = "https://files.pythonhosted.org/packages/ef/7c/17d30673351439a6951bf54f564cf2443ab00ae264ec9df00e2efd710eb5/pydantic_core-2.46.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac5ec7fb9b87f04ee839af2d53bcadea57ded7d229719f56c0ed895bff987943", size = 2160691, upload-time = "2026-04-20T14:41:14.023Z" }, - { url = "https://files.pythonhosted.org/packages/86/66/af8adbcbc0886ead7f1a116606a534d75a307e71e6e08226000d51b880d2/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a3b11c812f61b3129c4905781a2601dfdfdea5fe1e6c1cfb696b55d14e9c054f", size = 2182543, upload-time = "2026-04-20T14:40:48.886Z" }, - { url = "https://files.pythonhosted.org/packages/b0/37/6de71e0f54c54a4190010f57deb749e1ddf75c568ada3b1320b70067f121/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1108da631e602e5b3c38d6d04fe5bb3bfa54349e6918e3ca6cf570b2e2b2f9d4", size = 2324513, upload-time = "2026-04-20T14:42:36.121Z" }, - { url = "https://files.pythonhosted.org/packages/51/b1/9fc74ce94f603d5ef59ff258ca9c2c8fb902fb548d340a96f77f4d1c3b7f/pydantic_core-2.46.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de885175515bcfa98ae618c1df7a072f13d179f81376c8007112af20567fd08a", size = 2361853, upload-time = "2026-04-20T14:43:24.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/d0/4c652fc592db35f100279ee751d5a145aca1b9a7984b9684ba7c1b5b0535/pydantic_core-2.46.3-cp310-cp310-win32.whl", hash = "sha256:d11058e3201527d41bc6b545c79187c9e4bf85e15a236a6007f0e991518882b7", size = 1980465, upload-time = "2026-04-20T14:44:46.239Z" }, - { url = "https://files.pythonhosted.org/packages/27/b8/a920453c38afbe1f355e1ea0b0d94a0a3e0b0879d32d793108755fa171d5/pydantic_core-2.46.3-cp310-cp310-win_amd64.whl", hash = "sha256:3612edf65c8ea67ac13616c4d23af12faef1ae435a8a93e5934c2a0cbbdd1fd6", size = 2073884, upload-time = "2026-04-20T14:43:01.201Z" }, - { url = "https://files.pythonhosted.org/packages/22/a2/1ba90a83e85a3f94c796b184f3efde9c72f2830dcda493eea8d59ba78e6d/pydantic_core-2.46.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ab124d49d0459b2373ecf54118a45c28a1e6d4192a533fbc915e70f556feb8e5", size = 2106740, upload-time = "2026-04-20T14:41:20.932Z" }, - { url = "https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c", size = 1948293, upload-time = "2026-04-20T14:43:42.115Z" }, - { url = "https://files.pythonhosted.org/packages/3e/b8/2e8e636dc9e3f16c2e16bf0849e24be82c5ee82c603c65fc0326666328fc/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c024e08c0ba23e6fd68c771a521e9d6a792f2ebb0fa734296b36394dc30390e", size = 1973222, upload-time = "2026-04-20T14:41:57.841Z" }, - { url = "https://files.pythonhosted.org/packages/34/36/0e730beec4d83c5306f417afbd82ff237d9a21e83c5edf675f31ed84c1fe/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6645ce7eec4928e29a1e3b3d5c946621d105d3e79f0c9cddf07c2a9770949287", size = 2053852, upload-time = "2026-04-20T14:40:43.077Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f0/3071131f47e39136a17814576e0fada9168569f7f8c0e6ac4d1ede6a4958/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a712c7118e6c5ea96562f7b488435172abb94a3c53c22c9efc1412264a45cbbe", size = 2221134, upload-time = "2026-04-20T14:43:03.349Z" }, - { url = "https://files.pythonhosted.org/packages/2f/a9/a2dc023eec5aa4b02a467874bad32e2446957d2adcab14e107eab502e978/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69a868ef3ff206343579021c40faf3b1edc64b1cc508ff243a28b0a514ccb050", size = 2279785, upload-time = "2026-04-20T14:41:19.285Z" }, - { url = "https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2", size = 2089404, upload-time = "2026-04-20T14:43:10.108Z" }, - { url = "https://files.pythonhosted.org/packages/2a/78/8692e3aa72b2d004f7a5d937f1dfdc8552ba26caf0bec75f342c40f00dec/pydantic_core-2.46.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:3481bd1341dc85779ee506bc8e1196a277ace359d89d28588a9468c3ecbe63fa", size = 2114898, upload-time = "2026-04-20T14:44:51.475Z" }, - { url = "https://files.pythonhosted.org/packages/6a/62/e83133f2e7832532060175cebf1f13748f4c7e7e7165cdd1f611f174494b/pydantic_core-2.46.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8690eba565c6d68ffd3a8655525cbdd5246510b44a637ee2c6c03a7ebfe64d3c", size = 2157856, upload-time = "2026-04-20T14:43:46.64Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ec/6a500e3ad7718ee50583fae79c8651f5d37e3abce1fa9ae177ae65842c53/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4de88889d7e88d50d40ee5b39d5dac0bcaef9ba91f7e536ac064e6b2834ecccf", size = 2180168, upload-time = "2026-04-20T14:42:00.302Z" }, - { url = "https://files.pythonhosted.org/packages/d8/53/8267811054b1aa7fc1dc7ded93812372ef79a839f5e23558136a6afbfde1/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:e480080975c1ef7f780b8f99ed72337e7cc5efea2e518a20a692e8e7b278eb8b", size = 2322885, upload-time = "2026-04-20T14:41:05.253Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c1/1c0acdb3aa0856ddc4ecc55214578f896f2de16f400cf51627eb3c26c1c4/pydantic_core-2.46.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de3a5c376f8cd94da9a1b8fd3dd1c16c7a7b216ed31dc8ce9fd7a22bf13b836e", size = 2360328, upload-time = "2026-04-20T14:41:43.991Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d0/ef39cd0f4a926814f360e71c1adeab48ad214d9727e4deb48eedfb5bce1a/pydantic_core-2.46.3-cp311-cp311-win32.whl", hash = "sha256:fc331a5314ffddd5385b9ee9d0d2fee0b13c27e0e02dad71b1ae5d6561f51eeb", size = 1979464, upload-time = "2026-04-20T14:43:12.215Z" }, - { url = "https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl", hash = "sha256:b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346", size = 2070837, upload-time = "2026-04-20T14:41:47.707Z" }, - { url = "https://files.pythonhosted.org/packages/9f/1e/264a17cd582f6ed50950d4d03dd5fefd84e570e238afe1cb3e25cf238769/pydantic_core-2.46.3-cp311-cp311-win_arm64.whl", hash = "sha256:5dfd51cf457482f04ec49491811a2b8fd5b843b64b11eecd2d7a1ee596ea78a6", size = 2053647, upload-time = "2026-04-20T14:42:27.535Z" }, - { url = "https://files.pythonhosted.org/packages/4b/cb/5b47425556ecc1f3fe18ed2a0083188aa46e1dd812b06e406475b3a5d536/pydantic_core-2.46.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b11b59b3eee90a80a36701ddb4576d9ae31f93f05cb9e277ceaa09e6bf074a67", size = 2101946, upload-time = "2026-04-20T14:40:52.581Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089", size = 1951612, upload-time = "2026-04-20T14:42:42.996Z" }, - { url = "https://files.pythonhosted.org/packages/50/6e/b7348fd30d6556d132cddd5bd79f37f96f2601fe0608afac4f5fb01ec0b3/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a519dab6d63c514f3a81053e5266c549679e4aa88f6ec57f2b7b854aceb1b0", size = 1977027, upload-time = "2026-04-20T14:42:02.001Z" }, - { url = "https://files.pythonhosted.org/packages/82/11/31d60ee2b45540d3fb0b29302a393dbc01cd771c473f5b5147bcd353e593/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6cd87cb1575b1ad05ba98894c5b5c96411ef678fa2f6ed2576607095b8d9789", size = 2063008, upload-time = "2026-04-20T14:44:17.952Z" }, - { url = "https://files.pythonhosted.org/packages/8a/db/3a9d1957181b59258f44a2300ab0f0be9d1e12d662a4f57bb31250455c52/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f80a55484b8d843c8ada81ebf70a682f3f00a3d40e378c06cf17ecb44d280d7d", size = 2233082, upload-time = "2026-04-20T14:40:57.934Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e1/3277c38792aeb5cfb18c2f0c5785a221d9ff4e149abbe1184d53d5f72273/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3861f1731b90c50a3266316b9044f5c9b405eecb8e299b0a7120596334e4fe9c", size = 2304615, upload-time = "2026-04-20T14:42:12.584Z" }, - { url = "https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395", size = 2094380, upload-time = "2026-04-20T14:43:05.522Z" }, - { url = "https://files.pythonhosted.org/packages/a1/20/abac35dedcbfd66c6f0b03e4e3564511771d6c9b7ede10a362d03e110d9b/pydantic_core-2.46.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:367508faa4973b992b271ba1494acaab36eb7e8739d1e47be5035fb1ea225396", size = 2135429, upload-time = "2026-04-20T14:41:55.549Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a5/41bfd1df69afad71b5cf0535055bccc73022715ad362edbc124bc1e021d7/pydantic_core-2.46.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ad3c826fe523e4becf4fe39baa44286cff85ef137c729a2c5e269afbfd0905d", size = 2174582, upload-time = "2026-04-20T14:41:45.96Z" }, - { url = "https://files.pythonhosted.org/packages/79/65/38d86ea056b29b2b10734eb23329b7a7672ca604df4f2b6e9c02d4ee22fe/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec638c5d194ef8af27db69f16c954a09797c0dc25015ad6123eb2c73a4d271ca", size = 2187533, upload-time = "2026-04-20T14:40:55.367Z" }, - { url = "https://files.pythonhosted.org/packages/b6/55/a1129141678a2026badc539ad1dee0a71d06f54c2f06a4bd68c030ac781b/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:28ed528c45446062ee66edb1d33df5d88828ae167de76e773a3c7f64bd14e976", size = 2332985, upload-time = "2026-04-20T14:44:13.05Z" }, - { url = "https://files.pythonhosted.org/packages/d7/60/cb26f4077719f709e54819f4e8e1d43f4091f94e285eb6bd21e1190a7b7c/pydantic_core-2.46.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aed19d0c783886d5bd86d80ae5030006b45e28464218747dcf83dabfdd092c7b", size = 2373670, upload-time = "2026-04-20T14:41:53.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7e/c3f21882bdf1d8d086876f81b5e296206c69c6082551d776895de7801fa0/pydantic_core-2.46.3-cp312-cp312-win32.whl", hash = "sha256:06d5d8820cbbdb4147578c1fe7ffcd5b83f34508cb9f9ab76e807be7db6ff0a4", size = 1966722, upload-time = "2026-04-20T14:44:30.588Z" }, - { url = "https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1", size = 2072970, upload-time = "2026-04-20T14:42:54.248Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f8/a989b21cc75e9a32d24192ef700eea606521221a89faa40c919ce884f2b1/pydantic_core-2.46.3-cp312-cp312-win_arm64.whl", hash = "sha256:f1f8338dd7a7f31761f1f1a3c47503a9a3b34eea3c8b01fa6ee96408affb5e72", size = 2035963, upload-time = "2026-04-20T14:44:20.4Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109, upload-time = "2026-04-20T14:41:24.219Z" }, - { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820, upload-time = "2026-04-20T14:42:14.236Z" }, - { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785, upload-time = "2026-04-20T14:42:31.648Z" }, - { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761, upload-time = "2026-04-20T14:40:37.093Z" }, - { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989, upload-time = "2026-04-20T14:42:59.254Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975, upload-time = "2026-04-20T14:41:32.287Z" }, - { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325, upload-time = "2026-04-20T14:42:47.685Z" }, - { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368, upload-time = "2026-04-20T14:41:22.766Z" }, - { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908, upload-time = "2026-04-20T14:42:52.044Z" }, - { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422, upload-time = "2026-04-20T14:43:29.55Z" }, - { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709, upload-time = "2026-04-20T14:42:16.134Z" }, - { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428, upload-time = "2026-04-20T14:41:10.974Z" }, - { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601, upload-time = "2026-04-20T14:41:42.128Z" }, - { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517, upload-time = "2026-04-20T14:43:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802, upload-time = "2026-04-20T14:43:38.507Z" }, - { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614, upload-time = "2026-04-20T14:44:38.374Z" }, - { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896, upload-time = "2026-04-20T14:40:53.996Z" }, - { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314, upload-time = "2026-04-20T14:41:49.446Z" }, - { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133, upload-time = "2026-04-20T14:42:23.574Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726, upload-time = "2026-04-20T14:44:22.816Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214, upload-time = "2026-04-20T14:44:48.792Z" }, - { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927, upload-time = "2026-04-20T14:41:40.196Z" }, - { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789, upload-time = "2026-04-20T14:41:15.868Z" }, - { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815, upload-time = "2026-04-20T14:44:25.152Z" }, - { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608, upload-time = "2026-04-20T14:41:28.933Z" }, - { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968, upload-time = "2026-04-20T14:42:03.962Z" }, - { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842, upload-time = "2026-04-20T14:41:35.52Z" }, - { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661, upload-time = "2026-04-20T14:41:00.042Z" }, - { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686, upload-time = "2026-04-20T14:43:16.471Z" }, - { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907, upload-time = "2026-04-20T14:43:31.732Z" }, - { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047, upload-time = "2026-04-20T14:42:37.982Z" }, - { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329, upload-time = "2026-04-20T14:43:49.655Z" }, - { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847, upload-time = "2026-04-20T14:42:10.737Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742, upload-time = "2026-04-20T14:40:44.262Z" }, - { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235, upload-time = "2026-04-20T14:41:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633, upload-time = "2026-04-20T14:44:33.271Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679, upload-time = "2026-04-20T14:44:41.065Z" }, - { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342, upload-time = "2026-04-20T14:42:50.144Z" }, - { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208, upload-time = "2026-04-20T14:42:08.133Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237, upload-time = "2026-04-20T14:42:25.412Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540, upload-time = "2026-04-20T14:40:40.313Z" }, - { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556, upload-time = "2026-04-20T14:41:09.427Z" }, - { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756, upload-time = "2026-04-20T14:41:25.717Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305, upload-time = "2026-04-20T14:43:18.627Z" }, - { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310, upload-time = "2026-04-20T14:41:01.778Z" }, - { url = "https://files.pythonhosted.org/packages/31/75/c1ee7cb5b2277fe1f95837a58fb692eeda7162494fc1ddccb5f23be1d7f6/pydantic_core-2.46.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fa3eb7c2995aa443687a825bc30395c8521b7c6ec201966e55debfd1128bcceb", size = 2112012, upload-time = "2026-04-20T14:44:35.697Z" }, - { url = "https://files.pythonhosted.org/packages/6b/da/fb883281703dc5f4d68cdc648c503c46c8af5726f428013178d657c75181/pydantic_core-2.46.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d08782c4045f90724b44c95d35ebec0d67edb8a957a2ac81d5a8e4b8a200495", size = 1953006, upload-time = "2026-04-20T14:43:33.878Z" }, - { url = "https://files.pythonhosted.org/packages/92/47/5d7d7d04204920771086d768ca51306e34b63a31ad80a5f82c9d38dea568/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:831eb19aa789a97356979e94c981e5667759301fb708d1c0d5adf1bc0098b873", size = 1980050, upload-time = "2026-04-20T14:41:03.154Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e9/1c52019dd95babcff1bffe40022e8541c0447b8e4a44596a979c7309fa0c/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4335e87c7afa436a0dfa899e138d57a72f8aad542e2cf19c36fb428461caabd0", size = 2057165, upload-time = "2026-04-20T14:43:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e0aac1f35baf80ff8f35d4fa58d85dce248324a764d950460cf5c0569758/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99421e7684a60f7f3550a1d159ade5fdff1954baedb6bdd407cba6a307c9f27d", size = 2225133, upload-time = "2026-04-20T14:41:07.334Z" }, - { url = "https://files.pythonhosted.org/packages/d5/df/8eb296b9661574ef26e990856991615c1e1fb1744a10b896bff684a3c99a/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd81f6907932ebac3abbe41378dac64b2380db1287e2aa64d8d88f78d170f51a", size = 2282409, upload-time = "2026-04-20T14:43:07.567Z" }, - { url = "https://files.pythonhosted.org/packages/d2/07/888c53b769be71072a1dbd741ec25160579d789ef276fd5bd2045a07b09a/pydantic_core-2.46.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f247596366f4221af52beddd65af1218797771d6989bc891a0b86ccaa019168", size = 2095775, upload-time = "2026-04-20T14:42:21.586Z" }, - { url = "https://files.pythonhosted.org/packages/8a/19/6c32bdb93362b899bf92ac42c824898ab93dd8a23f339cbf5c5098ef61a6/pydantic_core-2.46.3-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:6dff8cc884679df229ebc6d8eb2321ea6f8e091bc7d4886d4dc2e0e71452843c", size = 2118999, upload-time = "2026-04-20T14:44:43.791Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c6/31d926d003f59a032ff61b62990e378f0fb985aa9d77fd448a6140f7eb5f/pydantic_core-2.46.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68ef2f623dda6d5a9067ac014e406c020c780b2a358930a7e5c1b73702900720", size = 2162368, upload-time = "2026-04-20T14:41:12.529Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a1/48d669051947845ff54f5b7cb6254ccafb23aa284184de6f273145d0e45b/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d56bdb4af1767cc15b0386b3c581fdfe659bb9ee4a4f776e92c1cd9d074000d6", size = 2184786, upload-time = "2026-04-20T14:42:40.235Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/a722cade3d4bd8ed6433a8e84eddf2f993b5fb4a47cade269289cfb4cc0c/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:91249bcb7c165c2fb2a2f852dbc5c91636e2e218e75d96dfdd517e4078e173dd", size = 2325626, upload-time = "2026-04-20T14:41:37.73Z" }, - { url = "https://files.pythonhosted.org/packages/ad/8a/238e33805d6f3691bcce8739db7d0376a2388e1b3f2c8db2128f6683fc78/pydantic_core-2.46.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b068543bdb707f5d935dab765d99227aa2545ef2820935f2e5dd801795c7dbd", size = 2363286, upload-time = "2026-04-20T14:42:29.385Z" }, - { url = "https://files.pythonhosted.org/packages/ad/2e/ab044f4431798a565242d5e8e48d080faa931f45e3d30df62e4f501d9bb0/pydantic_core-2.46.3-cp39-cp39-win32.whl", hash = "sha256:dcda6583921c05a40533f982321532f2d8db29326c7b95c4026941fa5074bd79", size = 1981660, upload-time = "2026-04-20T14:43:54.525Z" }, - { url = "https://files.pythonhosted.org/packages/d0/2c/13855786276f51fe86915c0533f4fc14e1d5421726ba8ad57caa09eb18ec/pydantic_core-2.46.3-cp39-cp39-win_amd64.whl", hash = "sha256:a35cc284c8dd7edae8a31533713b4d2467dfe7c4f1b5587dd4031f28f90d1d13", size = 2078793, upload-time = "2026-04-20T14:42:17.87Z" }, - { url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683, upload-time = "2026-04-20T14:42:19.779Z" }, - { url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052, upload-time = "2026-04-20T14:43:59.274Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172, upload-time = "2026-04-20T14:41:17.469Z" }, - { url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596, upload-time = "2026-04-20T14:40:41.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877, upload-time = "2026-04-20T14:43:27.091Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428, upload-time = "2026-04-20T14:40:45.781Z" }, - { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550, upload-time = "2026-04-20T14:44:02.425Z" }, - { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657, upload-time = "2026-04-20T14:42:45.149Z" }, - { url = "https://files.pythonhosted.org/packages/1f/da/99d40830684f81dec901cac521b5b91c095394cc1084b9433393cde1c2df/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:13afdd885f3d71280cf286b13b310ee0f7ccfefd1dbbb661514a474b726e2f25", size = 2107973, upload-time = "2026-04-20T14:42:06.175Z" }, - { url = "https://files.pythonhosted.org/packages/99/a5/87024121818d75bbb2a98ddbaf638e40e7a18b5e0f5492c9ca4b1b316107/pydantic_core-2.46.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f91c0aff3e3ee0928edd1232c57f643a7a003e6edf1860bc3afcdc749cb513f3", size = 1947191, upload-time = "2026-04-20T14:43:14.319Z" }, - { url = "https://files.pythonhosted.org/packages/60/62/0c1acfe10945b83a6a59d19fbaa92f48825381509e5701b855c08f13db76/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6529d1d128321a58d30afcc97b49e98836542f68dd41b33c2e972bb9e5290536", size = 2123791, upload-time = "2026-04-20T14:43:22.766Z" }, - { url = "https://files.pythonhosted.org/packages/75/3e/3b2393b4c8f44285561dc30b00cf307a56a2eff7c483a824db3b8221ca51/pydantic_core-2.46.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:975c267cff4f7e7272eacbe50f6cc03ca9a3da4c4fbd66fffd89c94c1e311aa1", size = 2153197, upload-time = "2026-04-20T14:44:27.932Z" }, - { url = "https://files.pythonhosted.org/packages/ba/75/5af02fb35505051eee727c061f2881c555ab4f8ddb2d42da715a42c9731b/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2b8e4f2bbdf71415c544b4b1138b8060db7b6611bc927e8064c769f64bed651c", size = 2181073, upload-time = "2026-04-20T14:43:20.729Z" }, - { url = "https://files.pythonhosted.org/packages/10/92/7e0e1bd9ca3c68305db037560ca2876f89b2647deb2f8b6319005de37505/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e61ea8e9fff9606d09178f577ff8ccdd7206ff73d6552bcec18e1033c4254b85", size = 2315886, upload-time = "2026-04-20T14:44:04.826Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d8/101655f27eaf3e44558ead736b2795d12500598beed4683f279396fa186e/pydantic_core-2.46.3-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b504bda01bafc69b6d3c7a0c7f039dcf60f47fab70e06fe23f57b5c75bdc82b8", size = 2360528, upload-time = "2026-04-20T14:40:47.431Z" }, - { url = "https://files.pythonhosted.org/packages/07/0f/1c34a74c8d07136f0d729ffe5e1fdab04fbdaa7684f61a92f92511a84a15/pydantic_core-2.46.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b00b76f7142fc60c762ce579bd29c8fa44aaa56592dd3c54fab3928d0d4ca6ff", size = 2184144, upload-time = "2026-04-20T14:42:57Z" }, + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/5d/00/13a0c039569d1e583779ee1b8d7df6bfe275a0db83fcae14f01d6856c16e/pydantic_core-2.46.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae", size = 2115337, upload-time = "2026-05-06T13:38:37.741Z" }, + { url = "https://files.pythonhosted.org/packages/41/60/e70fa1ee03e243bdfd4b1fddf1e1f2a8fba681df3034b51b9376c0fb5bf5/pydantic_core-2.46.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201", size = 1957976, upload-time = "2026-05-06T13:37:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/11/9a/78fb5f2ea849f767ea802de8b4e8f5a0c4a48ddbe4bc66bd19ac2f55a01c/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0", size = 1979390, upload-time = "2026-05-06T13:36:52.419Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7d/3acfdcd000bad9735de0430a88355948469781f62cb841fd63e8a307e80e/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15", size = 2043263, upload-time = "2026-05-06T13:39:54.798Z" }, + { url = "https://files.pythonhosted.org/packages/35/60/1325e5a8d7f9697416481c7f7c1c304738d6b961a7fd1ea0f054ce0f14fb/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76", size = 2225708, upload-time = "2026-05-06T13:40:24.887Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b0/9ec8c38f33b26db0b612cb7fd165bb0a370773710432a2a74fa31287b430/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49", size = 2288494, upload-time = "2026-05-06T13:38:00.091Z" }, + { url = "https://files.pythonhosted.org/packages/65/05/497446a9586d1b2d24ee25ebe208beb15388f1875d783e1e014055d150ac/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928", size = 2095629, upload-time = "2026-05-06T13:38:23.632Z" }, + { url = "https://files.pythonhosted.org/packages/93/d9/cd5fa98f9d94f9294c15459396c8a2383c164469e679ac178d6d42cfee6b/pydantic_core-2.46.4-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066", size = 2119309, upload-time = "2026-05-06T13:39:50.144Z" }, + { url = "https://files.pythonhosted.org/packages/20/1b/64cec655451ddbf3976df5dc9706b240df4fdaebdeebeadd4f59a8dab926/pydantic_core-2.46.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6", size = 2170216, upload-time = "2026-05-06T13:39:14.561Z" }, + { url = "https://files.pythonhosted.org/packages/2a/21/fe9f039138c9ea3be10ccdb6ec490acb54dcbef5a5e96dbdf1411f82b929/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9", size = 2186726, upload-time = "2026-05-06T13:37:51.597Z" }, + { url = "https://files.pythonhosted.org/packages/44/cb/19ca0da64821d1aefcef65f253aa9ecbdd0dde360f607d0f9b3d95db2b4e/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29", size = 2320400, upload-time = "2026-05-06T13:39:36.29Z" }, + { url = "https://files.pythonhosted.org/packages/cd/14/fe3fbf6e845bf2080dc2f282d75085ddf79d037b35634ecde68f33c217b4/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9", size = 2363318, upload-time = "2026-05-06T13:38:53.039Z" }, + { url = "https://files.pythonhosted.org/packages/62/88/60b110889507a426eecf626f7536566cb290ada71147eff49b6e2724ca62/pydantic_core-2.46.4-cp39-cp39-win32.whl", hash = "sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1", size = 1988880, upload-time = "2026-05-06T13:39:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d6/8ede2f98f17e1e4e127d37be0eced4eee931a511c62cd68af50e1b25bfa9/pydantic_core-2.46.4-cp39-cp39-win_amd64.whl", hash = "sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac", size = 2079257, upload-time = "2026-05-06T13:39:38.498Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, ] [[package]] @@ -1412,7 +1494,7 @@ wheels = [ [[package]] name = "pyinstaller-hooks-contrib" -version = "2026.4" +version = "2026.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, @@ -1421,9 +1503,9 @@ dependencies = [ { name = "setuptools", version = "75.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "setuptools", version = "82.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/fe/9278c29394bf69169febc21f96b4252c3ee7c8ec22c2fc545004bed47e71/pyinstaller_hooks_contrib-2026.4.tar.gz", hash = "sha256:766c281acb1ecc32e21c8c667056d7ebf5da0aabd5e30c219f9c2a283620eeaa", size = 173050, upload-time = "2026-03-31T14:10:51.188Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/67/f4452d68793fb15beba4f19ef39a38a8822f0da7452b503c400d5a21f5c1/pyinstaller_hooks_contrib-2026.5.tar.gz", hash = "sha256:f066dfca8f7c45ff6336c9cf9fe25b4e48bfeb322a1aa24faaedfb8a8d1b0b08", size = 173689, upload-time = "2026-05-04T22:36:55.124Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/f4/035fb8c06deff827f540a9a4ed9122c54e5376fca3e42eddf0c263730775/pyinstaller_hooks_contrib-2026.4-py3-none-any.whl", hash = "sha256:1de1a5e49a878122010b88c7e295502bc69776c157c4a4dc78741a4e6178b00f", size = 455496, upload-time = "2026-03-31T14:10:49.867Z" }, + { url = "https://files.pythonhosted.org/packages/f6/5c/fd465d11da4d12b50d7eb5d2ee2ceb780d8d049dbb489f3828d131e387af/pyinstaller_hooks_contrib-2026.5-py3-none-any.whl", hash = "sha256:ea1535783fbdac4626351709e83f3ea80b681d3a4745763ebb407b5e27342eb9", size = 457314, upload-time = "2026-05-04T22:36:53.598Z" }, ] [[package]] @@ -1444,7 +1526,8 @@ version = "2.12.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, @@ -1459,7 +1542,8 @@ name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } @@ -1531,7 +1615,8 @@ version = "82.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } wheels = [ @@ -1654,10 +1739,11 @@ name = "sqlmodel" version = "0.0.34" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ - { name = "pydantic", version = "2.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "sqlalchemy", marker = "python_full_version == '3.9.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/6a/b1b26d589063e53a08c10a2d7bc624cba63dec045a312758d68f550a4ea1/sqlmodel-0.0.34.tar.gz", hash = "sha256:577e4aae1ba96ee5038e03d8b1404c642dad1a92e628988cdf4ce68d27abe982", size = 96236, upload-time = "2026-02-16T19:06:34.275Z" } @@ -1673,7 +1759,7 @@ resolution-markers = [ "python_full_version >= '3.10'", ] dependencies = [ - { name = "pydantic", version = "2.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pydantic", version = "2.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "sqlalchemy", marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] @@ -1703,7 +1789,8 @@ name = "starlette" version = "0.49.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, @@ -1802,7 +1889,8 @@ version = "4.15.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ @@ -1852,7 +1940,8 @@ version = "5.3.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "tzdata", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, @@ -1884,7 +1973,8 @@ name = "uvicorn" version = "0.39.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, @@ -1930,7 +2020,8 @@ name = "zipp" version = "3.23.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ @@ -2053,7 +2144,8 @@ version = "0.25.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version == '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } wheels = [